3

これはcachegrind出力の一部です。コードのこの部分は1224回実行されました。elmg1は、サイズ16 x 20のunsignedlongの配列です。私のマシンのL1キャッシュサイズは32KB、64Bキャッシュラインサイズ、8ウェイセットアソシアティブです。

  1. for(i = 0; i <20; i ++)78,336 2,448 2 50,184 0 0 1,224 0 0
  2. {{
  3. telm01 = elmg1 [i]; 146,880 0 0 73,440 0 0 24,480 0 0
  4. telm31 =(telm01 << 3)^ val1; 97,920 0 0 48,960 0 0 24,480 0 0
  5. telm21 =(telm01 << 2)^(val1 >> 1); 146,880 1,224 1 48,960 0 0 24,480 0 0
  6. telm11 =(telm01 << 1)^(val1 >> 2); 146,880 0 0 48,960 0 0 24,480 0 0
  7. }

A.ここに記載した理由は、forループ内の3行目に、I1ミスが多数見られるためです(L2ミスも1つ)。やや紛らわしく、理由がわかりませんでしたか?

B.コードの一部を最適化(時間)しようとしています。上記はほんの小さなスニペットです。私のプログラムのメモリアクセスでは、多くの費用がかかると思います。上記の例のように、elmg1は16x20サイズのunsignedlongの配列です。私がコードでそれを使おうとすると、常にいくつかのミスがあり、私のプログラムではこれらの変数が頻繁に発生します。助言がありますか?

C.これらの署名されていないlongを割り当てて(場合によっては初期化する)必要があります。calloc宣言と配列宣言のどちらを優先するかを提案してから、明示的に初期化してください。ちなみに、キャッシュがそれらを処理する方法に違いはありますか?

ありがとう。

4

1 に答える 1

3

ループを展開しようとしましたか?

  1. 今のところL1ミスについては心配していません。また、1224回のうち1回のL2ミスは問題ありません。CPUは、ある時点で値をキャッシュにロードする必要があります。
  2. このコードは、プログラムの他の部分と比較して、L2ミスの何パーセントの費用がかかりますか?
  3. calloc()を使用します。配列のサイズが常に同じで、サイズに定数を使用する場合、コンパイラーは配列のゼロ化を最適化できます。また、キャッシュラインの使用に影響を与える唯一のものは、初期化の方法ではなく、アライメントです。

編集:そのように読みにくく、最初に間違って読んだ数。

5行目の数字を正しく読んでいることを確認しましょう。

Ir    146,880
I1mr  1,224
ILmr  1
Dr    48,960
D1mr  0
DLmr  0
Dw    24,480
D1mw  0
DLmw  0

L1キャッシュは、コードI1用とデータD1用の2つの32Kバイトキャッシュに分割されます。IL&DLは、データと命令の両方で共有されるL2またはL3キャッシュです。

多数のI1mrは、データミスではなく命令ミスです。これは、ループコードがI1命令キャッシュから排出されていることを意味します。

I1は1行目と5行目で合計3672、つまり1224の3倍です。したがって、ループが実行されるたびに、64バイトのキャッシュラインで3つのI1キャッシュミスが発生します。つまり、ループコードサイズは128〜192バイトで、3つのキャッシュラインをカバーします。したがって、5行目でI1が欠落しているのは、ループコードが最後のキャッシュ行と交差する場所だからです。

cachegrindの結果を表示するには、KCachegrindを使用することをお勧めします

編集:キャッシュラインの詳細。

そのループコードは、それ自体で1224回呼び出されているようには見えないため、このコードをI1キャッシュからプッシュしているコードがさらにあることを意味します。

32KバイトのI1キャッシュは、512のキャッシュライン(それぞれ64バイト)に分割されます。「8ウェイセットアソシアティブ」の部分は、各メモリアドレスがそれらの512のキャッシュラインのうち8つだけにマップされることを意味します。プロファイルしているプログラム全体が32Kバイトのメモリの1つの連続したブロックである場合、それはすべてI1キャッシュに収まり、排出されるものはありません。これはほとんどの場合そうではなく、同じ8つのキャッシュラインに対して8つ以上の64バイトブロックのコードコンテンツが存在します。プログラム全体に1Mバイトのコード(これにはライブラリが含まれます)があるとすると、8つのキャッシュラインの各グループには、同じ8つのキャッシュラインに対応する約32(1Mバイト/ 32Kバイト)のコードが含まれます。

CPUキャッシュに関するすべての厄介な詳細については、このlwn.netの記事をお読みください

コンパイラは、プログラムのどの関数がホットスポット(何度も呼び出される)であり、どの関数がコードスポット(つまり、ほとんど実行されないエラーハンドラコード)であるかを常に検出できるとは限りません。GCCにはホット/コールド関数属性があり、関数をホット/コールドとしてマークできます。これにより、コンパイラはホット関数を1つのメモリブロックにグループ化して、キャッシュの使用率を向上させることができます(つまり、コールドコードがホットコードをプッシュしません)キャッシュの)。

とにかく、それらのI1ミスは、心配する時間の価値がありません。

于 2010-11-01T07:43:59.103 に答える