2

C(またはおそらく一般的に)では、算術を使用して値を取得するか、配列/変数から呼び出す方が高速ですか?

たとえば、私が持っていた場合

int myarray[7] = {16};
int mysixteen = 16;

次に、さまざまな方法で 16 を取得できます

myarray[#]
mysixteen
16
1 << 4
10 + 6

論理的には 16 が最速ですが、これは一連の数値にとって常に便利またはもっともらしいとは限りません。これが関連する可能性がある場所の例は、テーブルの事前計算です。64ビットのビットマスクが必要だとすると、配列を埋めることができます

for (int i = 0; i < 64; ++i) {
    mask[i] = 1 << i;
}

将来的に配列を呼び出すか、マクロを作成します

#define mask(b) (1 << b)

そしてそれを呼び出します。

4

2 に答える 2

2

一般に、いずれか

  • 16
  • 1 << 4
  • 10 + 6

コンパイラは定数折りたたみ16と呼ばれる最適化を確実に実装するため、結果はリテラルになります。

の性能

  • マイシックスティーン
  • myarray[n]

これらの変数の値が格納されている場所によっては、おそらく低くなります。記憶に?もしそうなら、メモリはいずれかの CPU キャッシュにありますか? それとも、CPUレジスタの1つに格納されていますか? 決定的な答えはありません。

一般に、特定のプログラムについては、コンパイラが提供するものをいつでも確認できますが、これは周囲のコードと最適化フラグによって大きく変わる可能性があることに注意してください。

自分で試してみるには、次の小さなプログラムを検討してください。

int f() { return 16; }

int g() { return 1 << 4; }

int h() { return 10 + 6; }

int i() {
    int myarray[7] = { 16 };
    return myarray[3];
}

int j() {
    int mysixteen = 16;
    return mysixteen;
}

gcc 4.7.2 を使用してコンパイルし、逆アセンブリをチェックすると、次のようになります。

$ gcc -c so19802742.c -o so19802742.o
$ objdump --disassemble so19802742.o

私はこれを得る:

0000000000000000 <f>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   b8 10 00 00 00          mov    $0x10,%eax
   9:   5d                      pop    %rbp
   a:   c3                      retq   

000000000000000b <g>:
   b:   55                      push   %rbp
   c:   48 89 e5                mov    %rsp,%rbp
   f:   b8 10 00 00 00          mov    $0x10,%eax
  14:   5d                      pop    %rbp
  15:   c3                      retq   

0000000000000016 <h>:
  16:   55                      push   %rbp
  17:   48 89 e5                mov    %rsp,%rbp
  1a:   b8 10 00 00 00          mov    $0x10,%eax
  1f:   5d                      pop    %rbp
  20:   c3                      retq   

0000000000000021 <i>:
  21:   55                      push   %rbp
  22:   48 89 e5                mov    %rsp,%rbp
  25:   48 c7 45 e0 00 00 00    movq   $0x0,-0x20(%rbp)
  2c:   00 
  2d:   48 c7 45 e8 00 00 00    movq   $0x0,-0x18(%rbp)
  34:   00 
  35:   48 c7 45 f0 00 00 00    movq   $0x0,-0x10(%rbp)
  3c:   00 
  3d:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
  44:   c7 45 e0 10 00 00 00    movl   $0x10,-0x20(%rbp)
  4b:   8b 45 ec                mov    -0x14(%rbp),%eax
  4e:   5d                      pop    %rbp
  4f:   c3                      retq   

0000000000000050 <j>:
  50:   55                      push   %rbp
  51:   48 89 e5                mov    %rsp,%rbp
  54:   c7 45 fc 10 00 00 00    movl   $0x10,-0x4(%rbp)
  5b:   8b 45 fc                mov    -0x4(%rbp),%eax
  5e:   5d                      pop    %rbp
  5f:   c3                      retq   

定数の折りたたみによりfghがまったく同じマシン コードを生成することに注意してください。での配列アクセスはi、ほとんどのマシン コード (ただし、必ずしも最も遅いとjは限りません!) を引き起こし、その中間のようなものです。

ただし、これには複雑なコードの最適化はまったくありません。eg でコンパイルしたときに生成されるコードは、まったく-O2異なる場合があります。これは、コンパイラが、5 つの関数のいずれかの呼び出しが定数!16

于 2013-11-18T13:41:18.093 に答える
0

これらのことについて心配する必要はありません。ほとんどの場合、コンパイラは十分にスマートです。乗算などの基本的な操作でさえ、シフトを使用するように最適化されることがあります。この方法の方が効率的です。

あなたの例について言えば、配列バージョンでは大量のメモリアクセスが必要になり、非常に遅くなります。アクセス数にもよりますが、ほとんどの場合、マクロの方が高速です。

于 2013-11-18T13:28:29.920 に答える