5

たとえば、ゲームを最適化するために配列 SQRT[i] を使用して平方根テーブルを作成したいのですが、SQRT[i] の値にアクセスするときに次の初期化でパフォーマンスの違いがあるかどうかはわかりません。

  1. ハードコード配列

    int SQRT[]={0,1,1,1,2,2,2,2,2,3,3,.......255,255,255}
    
  2. 実行時に値を生成する

    int SQRT[65536];
    int main(){
        for(int i=0;i<65536;i++){
            SQRT[i]=sqrt(i);
        }
        //other code
        return 0;
    }
    

それらにアクセスするいくつかの例:

    if(SQRT[a*a+b*b]>something)
    ...

プログラムがハードコード配列を別の方法で保存またはアクセスするかどうかはわかりません。また、コンパイラがハードコード配列を最適化してアクセス時間を短縮するかどうかもわかりません。それらの間にパフォーマンスの違いはありますか配列にアクセスするとき?

4

3 に答える 3

1

人々がコメントで言ったように:

if(SQRT[a*a+b*b]>something)

恐ろしいユースケースの例です。それが SQRT に必要なすべての場合は、単に square something.

何もエイリアスしないことをコンパイラに伝えることができる限りSQRT、実行時ループは実行可能ファイルを小さくし、起動時にわずかな CPU オーバーヘッドを追加するだけです。絶対に使わuint8_tないでintください。8 ビットのメモリ ロケーションから 32 ビットのテンポラリをロードすることは、ゼロで埋められた 32b のメモリ ロケーションからロードするよりも遅くはありません。(メモリ オペランドを使用する代わりに、x86 で余分なmovsx命令を使用すると、キャッシュ汚染が減少するという点で十分な代償を払うことができます。いずれにせよ、RISC マシンは通常、メモリ オペランドを許可しないため、値をレジスタにロードする命令が常に必要です。 )

また、sqrtSandybridge でのレイテンシは 10 ~ 21 サイクルです。頻繁に必要としない場合、int->double、sqrt、double->int チェーンは、L2 キャッシュ ヒットよりもそれほど悪くはありません。そして、L3 やメイン メモリに移動するよりも優れています。多くの が必要な場合はsqrt、必ず LUT を作成してください。テーブル内でバウンスして L1 ミスを引き起こしている場合でも、スループットは大幅に向上します。

次のようなもので、sqrtingの代わりにsquaringすることで初期化を最適化できます

uint8_t sqrt_lookup[65536];
void init_sqrt (void)
{
    int idx = 0;
    for (int i=0 ; i < 256 ; i++) {
        // TODO: check that there isn't an off-by-one here
        int iplus1_sqr = (i+1)*(i+1);
        memset(sqrt_lookup+idx, i, iplus1_sqr-idx);
        idx = iplus1_sqr;
    }
}

存在することの利点を引き続き得ることができますsqrt_lookup(constコンパイラーはエイリアスできないことを知っています)。を使用するかrestrictコンパイラに嘘をつくので、テーブルのユーザーにはconst配列が表示されますが、実際には配列に書き込みます。

extern constこれには、コンパイラを初期化するファイルではなく、ほとんどの場所で宣言することにより、コンパイラに嘘をつくことが含まれる場合があります。これが実際に機能し、2 つの異なるシンボルを参照するコードを作成しないことを確認する必要があります。それを初期化する関数で単にキャストするconstと、コンパイラーがそれを配置した場合にセグメンテーション違反が発生する可能性があります(または、一部のプラットフォームで可能であれば、初期化されていない場合はrodata読み取り専用メモリー?)bss

おそらく、次のようにして、コンパイラに嘘をつくことを避けることができます。

uint8_t restrict private_sqrt_table[65536];  // not sure about this use of restrict, maybe that will do it?
const uint8_t *const sqrt_lookup = private_sqrt_table;

実際には、これは単なるデータconstへのポインターconstであり、それが指しているものが別の参照によって変更できないという保証ではありません。

于 2015-08-14T00:59:03.200 に答える