考虑简单的代码:
UINT64 result;
UINT32 high, low;
...
result = ((UINT64)high << 32) | (UINT64)low;
现代编译器会将其转化为真正的高位桶形移位,还是将其优化为一个简单的副本到正确的位置?
如果不是,那么使用工会似乎比大多数人似乎使用的转变更有效。但是,让编译器对此进行优化是理想的解决方案。
我想知道当人们确实需要额外的一点性能时,我应该如何建议他们。
考虑简单的代码:
UINT64 result;
UINT32 high, low;
...
result = ((UINT64)high << 32) | (UINT64)low;
现代编译器会将其转化为真正的高位桶形移位,还是将其优化为一个简单的副本到正确的位置?
如果不是,那么使用工会似乎比大多数人似乎使用的转变更有效。但是,让编译器对此进行优化是理想的解决方案。
我想知道当人们确实需要额外的一点性能时,我应该如何建议他们。
我写了以下(希望是有效的)测试:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
void func(uint64_t x);
int main(int argc, char **argv)
{
#ifdef UNION
union {
uint64_t full;
struct {
uint32_t low;
uint32_t high;
} p;
} result;
#define value result.full
#else
uint64_t result;
#define value result
#endif
uint32_t high, low;
if (argc < 3) return 0;
high = atoi(argv[1]);
low = atoi(argv[2]);
#ifdef UNION
result.p.high = high;
result.p.low = low;
#else
result = ((uint64_t) high << 32) | low;
#endif
// printf("%08x%08x\n", (uint32_t) (value >> 32), (uint32_t) (value & 0xffffffff));
func(value);
return 0;
}
运行未优化输出的差异gcc -s
:
< mov -4(%rbp), %eax
< movq %rax, %rdx
< salq $32, %rdx
< mov -8(%rbp), %eax
< orq %rdx, %rax
< movq %rax, -16(%rbp)
---
> movl -4(%rbp), %eax
> movl %eax, -12(%rbp)
> movl -8(%rbp), %eax
> movl %eax, -16(%rbp)
我不知道汇编,所以我很难分析。但是,在非联合(顶级)版本上,似乎正在发生一些变化。
但是-O2
启用优化后,输出是相同的。因此生成了相同的代码,两种方式将具有相同的性能。
(Linux/AMD64 上的 gcc 版本 4.5.2)
-O2
有或没有联合的优化代码的部分输出:
movq 8(%rsi), %rdi
movl $10, %edx
xorl %esi, %esi
call strtol
movq 16(%rbx), %rdi
movq %rax, %rbp
movl $10, %edx
xorl %esi, %esi
call strtol
movq %rbp, %rdi
mov %eax, %eax
salq $32, %rdi
orq %rax, %rdi
call func
该片段在该行生成的跳转之后立即开始if
。
现代编译器比你想象的要聪明;-)(所以是的,我认为你可以期待任何体面的编译器的桶式转变)。
无论如何,我会使用语义更接近您实际尝试做的选项。
如果这应该是平台独立的,那么唯一的选择就是在这里使用班次。
union { r64; struct{low;high}}
您无法确定将映射到哪些低/高字段。想想字节序。
现代编译器可以很好地处理这种转变。
编辑:此响应基于没有演员表的 OP 代码的早期版本
这段代码
result = (high << 32) | low;
实际上会产生未定义的结果......因为high
您将 32 位值移动 32 位(值的宽度),结果将是未定义的,并且取决于编译器和操作系统平台的方式决定处理班次。然后,未定义移位的结果将low
是 or'd with ,这将再次是未定义的,因为您将未定义的值与已定义的值进行或,因此最终结果很可能不是 64 位值像你要的那样。例如,gcc -s
在 OSX 10.6 上发出的代码如下所示:
movl -4(%rbp), %eax //retrieving the value of "high"
movl $32, %ecx
shal %cl, %eax //performing the 32-bit shift on "high"
orl -8(%rbp), %eax //OR'ing the value of "low" to the shift op result
因此,您可以看到移位仅发生在具有 32 位汇编命令的 32 位寄存器中的 32 位值上……结果最终与根本没有任何移位完全相同,high | low
因为在这个情况下,shal $32, %eax
只返回原来在EAX
. 你没有得到 64 位的结果。
为了避免这种情况,high
请转换为uint64_t
类似:
result = ((uint64_t)high << 32) | low;