联合是否比现代编译器更有效?

问题描述

| 考虑简单的代码
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;
    

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...