考虑简单的代码:
UINT64 result;
UINT32 high, low;
...
result = ((UINT64)high << 32) | (UINT64)low;
现代编译器是否会将其转换为真正的高位移位,或者将其优化为简单的副本到正确的位置?
如果没有,那么使用联合似乎比大多数人似乎使用的转变更有效。但是,让编译器优化它是理想的解决方案。
我想知道当他们需要额外的一点性能时,我应该如何建议他们。
答案 0 :(得分:4)
现代编译器比你想象的更聪明;-)(所以是的,我认为你可以期待任何体面的编译器发生桶式转换。)
无论如何,我会使用具有更接近你实际尝试的语义的选项。
答案 1 :(得分:4)
如果这应该是平台无关的,那么唯一的选择就是在这里使用轮班。
使用union { r64; struct{low;high}}
,您无法确定哪些低/高字段将映射到。考虑一下endianess。
现代编译器非常善于处理这种转变。
答案 2 :(得分:4)
我写了以下(希望是有效的)测试:
#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版)
带或不带union的优化-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
行生成的跳转后立即开始。
答案 3 :(得分:2)
编辑:此回复基于OP代码的早期版本,没有投射
此代码
result = (high << 32) | low;
实际上会有未定义的结果...因为使用high
你将32位值移动32位(值的宽度),结果将是未定义的并且将会取决于编译器和OS平台如何决定处理这种转变。然后,未定义的移位的结果将与low
一起使用,这也将是未定义的,因为您对定义的值进行了或未定义的值,因此最终结果很可能不是一个像你想要的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;