问题描述
|
考虑简单的代码:
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;
}
运行未优化输出the2 un的差异:
< 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),-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,%esi
call strtol
movq %rbp,%rdi
mov %eax,%eax
salq $32,%rdi
orq %rax,%rdi
call func
片段由if
线产生的跳转后立即开始。
,现代编译器比您想像的要聪明;-)(所以,是的,我认为您可以期望任何不错的编译器都会发生重大变化)。
无论如何,我将使用语义更接近您实际尝试执行的选项。
,如果这应该是平台无关的,那么唯一的选择就是在这里使用shift。
使用union { r64; struct{low;high}}
,您无法确定将映射到哪个低/高场。考虑一下耐力。
现代编译器很好地处理了这种变化。
,编辑:此响应基于没有强制转换的OP的代码的早期版本
此代码
result = (high << 32) | low;
实际上会产生未定义的结果...因为使用high
,您要将32位值移动32位(值的宽度),结果将是不确定的,并且将取决于编译器和OS平台决定应对这一转变。然后,该不确定的移位的结果将与ѭ11或,由于您将一个未定义的值与定义的值进行比对,因此它也将是不确定的,因此最终结果很可能不是随心所欲的64位值。例如,OSX 10.6上的“ 2”发出的代码如下:
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位值上……结果最终与ѭ14same完全相同,而没有任何移位,因为在这种情况下,shal $32,%eax
只是返回in16ѭ中的原始值。您没有得到64位结果。
为了避免这种情况,请将high
转换为uint64_t
,例如:
result = ((uint64_t)high << 32) | low;