3

ステートメント

a[i] += a[j] * a[k];

数千回から数百万回実行される可能性のあるループで数千回実行されます。のエントリへのランダム アクセスを表すインデックスij、およびは、ステートメントで設定できます。ka

i = i_index[l];
j = j_index[l];
k = k_index[l];

wherelforループのインデックスです。整数配列i_indexj_index、およびk_indexは、プログラムの最初に設定され、時々変更される場合があります。

メモリ ポインター配列は代替手段です。例えば

*ap1[l] += *ap2[l] * (*ap3[l]);

ここで、メモリ ポインタ配列ap1ap2、およびは、 、、および配列ap3によって最初に識別された場所を指すように事前に設定されています。また、不定期に変更する場合があります。i_indexj_indexk_index

最初の方法は 2 番目の方法よりもきれいに見えますが、コンパイラに追加情報を提供する何らかの方法がない限り、遅くなるように見えます。XCode の GCC コンパイラーは、 、 、および 、 、および がほとんどの場合変更されないことを事前に発見するi_index方法j_indexk_indexないap1ようap2ですap3。パフォーマンスを向上させるために gcc コンパイラに注意を向ける方法はありますか?

4

1 に答える 1

3

コードの2番目のバージョンで明示的にキャッシュして保存しているコードの最初のバージョンで、コンパイラーにポインター値を効果的にキャッシュさせるオプションが見つかる可能性はほとんどありません。これは、コンパイラがこれらの値をキャッシュするために非常に大きなデータ構造を生成および保存するためのコードを発行する必要があるためであり、これは一般的なコンパイラの動作ではありません。

ただし、ターゲットとするアーキテクチャによっては、これはおそらく問題になりません。a多くのアーキテクチャには、へのアクセスに使用される「間接ベース+インデックス」アドレッシングモードがa[i] += a[j] * a[k];あり、最新のコアでは、プレーンな「間接」アドレッシングモード(つまり、1つ)に比べてパフォーマンスが低下することはありません。命令はiのサイズで乗算a[0]され、のベースアドレスに追加されa、結果を逆参照します)。ターゲットアーキテクチャのプロファイルを作成して、を参照してください。

どちらのバージョンでも状況を改善できる可能性がstructあるのは、3つの別々の配列の代わりにsの配列を使用して、の各値に必要な3つの値がlメモリに連続して保持されるようにすることです。

i = index[l].i;
j = index[l].j;
k = index[l].k;

また

*ap[l].i += *ap[l].j * *ap[l].k;

これは、コードが3つの同時線形トラバースではなく、indexまたは配列を介して1つの線形トラバースを作成していることを意味します。これは、プリフェッチャーが実行していることを認識するのに役立ちます。ap

于 2012-06-04T00:50:46.923 に答える