Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/150.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 位字段是否比掩蔽位和手工提取数据更有效(计算上)?_C++_C_Bit Manipulation - Fatal编程技术网

C++ 位字段是否比掩蔽位和手工提取数据更有效(计算上)?

C++ 位字段是否比掩蔽位和手工提取数据更有效(计算上)?,c++,c,bit-manipulation,C++,C,Bit Manipulation,我有大量的小数据块,我希望能够将它们放入一个更大的数据类型中。假设这是一个日期和时间。最明显的方法是通过这样的位字段 struct dt { unsigned long minute :6; unsigned long hour :5; unsigned long day :5; unsigned long month :4; unsigned long year :12; }stamp; dt t1(getTimeStamp()); dt t2(get

我有大量的小数据块,我希望能够将它们放入一个更大的数据类型中。假设这是一个日期和时间。最明显的方法是通过这样的位字段

struct dt
{
    unsigned long minute :6;
    unsigned long hour :5;
    unsigned long day :5;
    unsigned long month :4;
    unsigned long year :12;
}stamp;
dt t1(getTimeStamp());
dt t2(getTimeStamp());

if (t1 < t2)
{    std::cout << "T1 happened before T2\n";
}
现在让我们假设这个东西是有序的,所以首先声明的东西比后面声明的东西具有更高的重要性,所以如果我用变量的第一个字母来表示这些位,它看起来像:

mmmmmm|hhhhh|ddddd|mmmm|yyyyyyyyyyyy
最后,让我们假设我只是声明了一个无符号long,并使用掩码和移位将其拆分,以执行相同的操作

unsigned long dateTime;
这是我的问题:
就计算机需要做的事情而言,以下访问分钟、小时等的方法是否等效?或者是编译器/计算机对位字段使用了一些技巧性的方法

unsigned minutes = stamp.minutes;
//versus
unsigned minutes = ((dateTime & 0xf8000000)>>26;


等等。

这些可能会编译相同的机器代码,但如果这真的很重要,请对其进行基准测试。或者,更好的是,只使用位字段,因为它更容易

快速测试gcc产量:

shrq    $6, %rdi             ; using bit field
movl    %edi, %eax
andl    $31, %eax
vs


这是一个小小的endian机器,因此数字不同,但三条指令几乎相同。

编译器生成的指令与您显式写入以访问位的指令相同。因此,不要期望使用位字段会更快


事实上,严格地说,对于位字段,您无法控制它们在数据字中的位置(除非您的编译器提供一些额外的保证。我的意思是C99标准没有定义任何保证)。手动屏蔽时,您至少可以将两个最常访问的字段放在序列的第一位和最后一位,因为在这两个位置,隔离字段需要一次操作而不是两次操作。

仅当您的体系结构明确具有一组逐位操作和访问指令时。

视情况而定。如果使用位字段,那么编译器就会担心如何存储数据(位字段几乎完全由实现定义),这意味着:

  • 它可能会占用更多的空间,并且
  • 访问每个成员将高效完成
编译器通常会组织结构的布局,以便第二个假设成立,但代价是结构的总大小

编译器可能会在每个成员之间插入填充,以便于访问每个字段


另一方面,如果您只是将所有内容存储在一个
无符号long
(或一个字符数组)中,那么就由您来实现高效访问,但您可以保证布局。它将占用固定的大小,并且不会有填充。这意味着复制价值可能会降低成本。而且它的可移植性更高(假设您使用的是固定大小的int类型,而不仅仅是
无符号int
)。

在本例中,我将手动使用位字段。
但由于访问的原因,不是。但是因为能够比较两个dt。
最后,编译器总是会生成比您更好的代码(因为编译器会随着时间的推移变得更好,并且永远不会出错),但这段代码足够简单,您可能会编写最佳代码(但这是一种您不应该担心的微优化)

如果您的dt是一个整数,格式如下:

yyyyyyyyyyyy|mmmm|ddddd|hhhhh|mmmmmm
然后你自然可以这样比较它们

struct dt
{
    unsigned long minute :6;
    unsigned long hour :5;
    unsigned long day :5;
    unsigned long month :4;
    unsigned long year :12;
}stamp;
dt t1(getTimeStamp());
dt t2(getTimeStamp());

if (t1 < t2)
{    std::cout << "T1 happened before T2\n";
}
dtt1(getTimeStamp()); dtt2(getTimeStamp()); if(t1{std::cout它完全依赖于平台和编译器。一些处理器,特别是微控制器,具有位寻址指令或位寻址内存,如果使用内置语言结构,编译器可以直接使用这些指令或内存。如果在这样的处理器上使用位屏蔽操作位,编译器必须是smar然后,确定潜在的优化


在大多数桌面平台上,我建议您在为小事情操心,但是如果您需要知道,您应该通过分析或计时代码来测试它,或者分析生成的代码。请注意,根据编译器优化选项,甚至不同的编译器,您可能会得到非常不同的结果。

编译器有时可以组合在非直观的情况下访问位字段。我曾经在访问条件表达式中使用的1位条目时反汇编了生成的代码(sparc为gcc 3.4.6)。编译器融合了对位的访问,并与整数进行了比较。我将尝试重现这个想法(不在工作中,无法访问所涉及的源代码):

编译为与等效的内容(我知道我的示例中的位顺序正好相反,但这只是为了简化示例)

还有一点要考虑的是,如果你想使用比特字段(它们通常比比特KungFu更可读),如果你有一些空闲的比特,那么对于某些字段保留整个字节是有用的,从而简化了处理器的访问。


对齐的8位字段可以使用移动字节命令加载,并且不需要屏蔽步骤。

我会将年份放在高位,下一个月等。然后,当你想要比较对象时,你不需要提取年份来获得它们,整数上的正常<运算符将正常工作。这是一个非常聪明的技巧,我会我必须在将来记住它。不幸的是,我在编写的程序中没有存储日期和时间。在我的代码中,它被用来存储伽马射线能量、探测器数量和探测器效率校正值(因此,每次我需要它时,它不必从复杂的小公式中重新计算。)我花了一分钟才弄明白为什么最高有效位的东西会更快;但是,这是因为你只需要向下移位,而不是按位移位,然后再移位,对吗?@James,没错。那只是为了阅读。我是说
dt t1(getTimeStamp());
dt t2(getTimeStamp());

if (convertToInt(t1) < convertToInt(t2))
{    std::cout << "T1 happened before T2\n";
}
// or
if ((t1.year < t2.year)
    || ((t1.year == t2.year) && ((t1.month < t2.month)
      || ((t1.month == t2.month) && ((t1.day < t2.day)
        || ((t1.day == t2.day) && (t1.hour  etc.....
struct bits {
  int b1:1;
  int b2:1;
  int b3:1;
  ...
} x;

if(x.b1 && x.b2 && !x.b3)
...
if(x.b2 && !x.b2 && x.b3)
temp = (x & 7);
if( temp == 6)
...
if( temp == 5)