我发现在某些答案中他们建议使用
lower = (some_var << 32) >> 32;
但我测试后发现以下内容更快:
lower = some_var & 0xffffffff;
哪个更好?在编译器优化后,前者在某些情况下更安全还是更快?
答案 0 :(得分:7)
使用&
屏蔽更好:
&
对于有符号和无符号some_var
是可靠的,而向右移位为负数会产生实现定义结果:E1的值&gt;&gt; E2是E1右移E2位位置。 [...]如果E1具有有符号类型和负值,则结果值是实现定义的。
屏蔽的一个缺点是,相对容易意外地有7或9 F
s,而32
中的拼写错误很明显:有其他方法可以生成屏蔽值,例如: (1LL<<32)-1
,或者是hackish,但有些优雅uint32_t(-1)
。
当然,如果lower
是uint32_t
和some_var
uint64_t
,您可以让转换隐含,因此优化工具甚至不需要实现bitwise-AND可以在赋值之前删除,但是这可能会给你一个编译器警告,你可以用它来沉默...
uint32_t lower = static_cast<uint32_t>(some_var);
当分配给另一个uint64_t
时,或者当掩码不是所有32个最低有效位时,屏蔽主要是有用的。
答案 1 :(得分:5)
使用AND进行屏蔽更好,它不依赖于值的签名。
但是,采用低32位的最有效方法是将其分配给32位变量。
uint64_t u = 0x1122334455667788;
uint32_t n;
n = static_cast<uint32_t>(u); // 0x55667788
与逐位AND的不同之处在于CPU只是在不执行任何逻辑操作的情况下占用较低的部分。
如果你有一个32位CPU,它只会忽略存储在第二个寄存器或存储器位置的上限值。
如果你有一个64位CPU,它有一条指令将32位值扩展(无符号)为64位值。
答案 2 :(得分:1)
一个好的优化器会在两种情况下生成相同的代码。对我来说,这是最直接的方法:lower = some_var & 0xffffffff;
另一种形式可能会产生不必要的移位。
当我想绝对确定编译器不会搞砸时,我有时会使用union来重叠变量。
例如:
typedef union {
int64 QWORD;
int32 DWORD[2];
} overlapper64;
overlapper someVariable;
然后将其作为:
访问someVariable.QWORD;
int32 myVar32 = someVariable.DWORD[0];
根据平台/编译器的不同,重叠的顺序可能会有所不同。 请务必在您的特定平台上进行测试。 在C中,我使用一堆特定于平台的#ifdefs来自动控制订单。
答案 3 :(得分:1)
添加其他人所说的,基于你使用第二个选项的编译器可能会更快,因为如果没有优化,第一个选项实现为2个cpu指令,而第二个选项是一个cpu指令。这可能是您使用第二个选项观察性能提升的原因。
答案 4 :(得分:0)
我认为两者都同样好。但是,如标准所示,使用按位运算符会更好(不确定是否存在性能差异)方法:
6.5.7按位移位运算符
4&lt;&lt;&lt;&lt;&lt;&lt; E2是E1 左移E2位位置;腾空 位用零填充。如果E1有 无符号类型,值的 结果是E1×2E2,减少模1 超过最大值 在结果类型中可表示。如果 E1具有签名类型和非负 值,E1×2E2是可表示的 在结果类型中,那就是 结果价值;否则, 行为未定义。