7

単純なコードを考えてみましょう。

UINT64 result;
UINT32 high, low;
...
result = ((UINT64)high << 32) | (UINT64)low;

最新のコンパイラーはそれを実際のバレルシフタに変えますか、それとも適切な場所への単純なコピーに最適化しますか?

そうでない場合は、ほとんどの人が使用しているように見えるシフトよりも、ユニオンを使用する方が効率的であるように思われます。ただし、コンパイラに最適化させることが理想的なソリューションです。

余分なパフォーマンスが必要な場合、どのようにアドバイスすればよいのでしょうか。

4

4 に答える 4

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)

-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スニペットは、ラインによって生成されたジャンプの直後に始まります。

于 2011-05-25T18:59:50.363 に答える
4

最新のコンパイラは、あなたが思っているよりも賢いです;-)(そうです、まともなコンパイラではバレルシフタが期待できると思います)。

とにかく、私はあなたが実際にやろうとしていることに近いセマンティクスを持つオプションを使用します。

于 2011-05-25T18:17:35.047 に答える
4

これがプラットフォームに依存しないと想定される場合、唯一のオプションはここでシフトを使用することです。

低/高フィールドがどのマップにマップされるかを判断することunion { r64; struct{low;high}}はできません。エンディアンについて考えてください。

最新のコンパイラは、このようなシフトをうまく処理できます。

于 2011-05-25T18:20:43.057 に答える
2

編集:この応答は、キャストがなかった以前のバージョンのOPのコードに基づいています

このコード

result = (high << 32) | low;

実際には未定義の結果になります... high32ビット値を32ビット(値の幅)だけシフトすると、結果は未定義になり、コンパイラとOSプラットフォームによって異なります。シフトを処理することを決定します。その未定義のシフトの結果は、でまたは'dになりlowます。これは、定義された値に対して未定義の値をまたは'しているため、再び未定義になります。したがって、最終結果は64ビット値ではない可能性があります。あなたが望むように。たとえば、gcc -sOSX10.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;
于 2011-05-25T19:04:44.107 に答える