4

私は Cachegrind を使用して、libc なしでコンパイルされた静的プログラムのキャッシュ ミスの数を取得してい_startます (asm でメイン関数と exit syscall を呼び出すだけです)。プログラムは完全に決定論的であり、命令とメモリ参照は実行ごとに変化しません。キャッシュは、置換ポリシーとして LRU と完全に関連付けられています。

ただし、ミスの数が時々変化することに気付きました。より具体的には、別のディレクトリに移動するまで、ミスの数は常に同じです。

 % cache=8 && valgrind --tool=cachegrind --I1=$((cache * 64)),$cache,64 --D1=$((cache * 64)),$cache,64 --L2=262144,4096,64 ./adpcm        
 ...
 ==31352== I   refs:      216,145,010
 ...
 ==31352== D   refs:      130,481,003  (95,186,001 rd   + 35,295,002 wr)
 ==31352== D1  misses:        240,004  (   150,000 rd   +     90,004 wr)
 ==31352== LLd misses:             31  (        11 rd   +         20 wr)

同じコマンドを何度も実行すると、同じ結果が得られます。しかし、別のディレクトリからこのプログラムを実行すると:

 % cd ..
 % cache=8 && valgrind --tool=cachegrind --I1=$((cache * 64)),$cache,64 --D1=$((cache * 64)),$cache,64 --L2=262144,4096,64 ./malardalen2/adpcm
 ...
 ==31531== I   refs:      216,145,010
 ...
 ==31531== D   refs:      130,481,003  (95,186,001 rd   + 35,295,002 wr)
 ==31531== D1  misses:        250,004  (   160,000 rd   +     90,004 wr)
 ==31531== LLd misses:             31  (        11 rd   +         20 wr)

また、別のディレクトリからの結果も異なります。

また、Pin ツールを使用していくつかの実験を行いましたが、これを使用すると、異なる値を取得するためにディレクトリを変更する必要はありません。しかし、可能な値のセットは非常に限られているようで、Cachegrind とまったく同じです。

私の質問は、そのような違いの原因は何でしょうか?

私の最初のヒントは、私のプログラムがメモリ内で同じように配置されていないことです。その結果、以前の実行で同じ行に格納されたいくつかの変数はもうありません。これは、組み合わせの数が限られていることも説明できます。しかし、キャッシュグラインド(およびピン)は仮想アドレスを使用していたと思いますが、OS(Linux)は常に同じ仮想アドレスを提供していると思います。他のアイデアはありますか?

編集: LLd ミスの読み取りを推測できるように、プログラムは 31 の異なるキャッシュ ラインのみを使用します。また、キャッシュには 8 つのキャッシュ ラインしか含めることができません。したがって、実際の場合でも、キャッシュが 2 回目に既に読み込まれているという考えでは、違いを説明できません (最大で、L1 にとどまることができるのは 8 ラインのみです)。

編集 2: Cachegrind のレポートは、実際のキャッシュ ミス (パフォーマンス カウンターによって与えられる) に基づくものではなく、シミュレーションの結果です。基本的に、ミスの数をカウントするためにキャッシュの動作をシミュレートします。結果は一時的なものに過ぎないため、それはまったく問題なく、キャッシュ プロパティ (サイズ、結合性) を変更できます。

編集 3:私が使用しているハードウェアは、Linux 3.2 x86_64 上の Intel Core i7 です。コンパイル フラグは -static で、一部のプログラムでは -nostdlib (IIRC、私は今家にいません) です。

4

2 に答える 2

2

これは valgrind の既知の動作のようです:

キャッシュ ベース アドレスを出力する例を使用し、レイアウトのランダム化も無効にしました。

実行可能ファイルを 2 回実行すると、両方の実行で同じ結果が得られました。

D   refs:       40,649  (28,565 rd   + 12,084 wr)
==15016== D1  misses:     11,465  ( 8,412 rd   +  3,053 wr)
==15016== LLd misses:      1,516  ( 1,052 rd   +    464 wr)
==15016== D1  miss rate:    28.2% (  29.4%     +   25.2%  )
==15016== LLd miss rate:     3.7% (   3.6%     +    3.8%  )

villar@localhost ~ $ cache=8 && valgrind --tool=cachegrind --I1=$((cache * 64)),$cache,64 --D1=$((cache * 64)),$cache,64 --L2=262144,4096,64 ./a.out 

==15019== D   refs:       40,649  (28,565 rd   + 12,084 wr)
==15019== D1  misses:     11,465  ( 8,412 rd   +  3,053 wr)
==15019== LLd misses:      1,516  ( 1,052 rd   +    464 wr)
==15019== D1  miss rate:    28.2% (  29.4%     +   25.2%  )
==15019== LLd miss rate:     3.7% (   3.6%     +    3.8%  )

cachegrind のドキュメントによると ( http://www.cs.washington.edu/education/courses/cse326/05wi/valgrind-doc/cg_main.html )

価値のないもう 1 つのことは、結果が非​​常にデリケートであることです。>valgrind.so ファイルのサイズ、プロファイリングされるプログラムのサイズ、またはその名前の長さを変更すると、結果が混乱する可能性があります。バリエーションはわずかですが、プログラムが少しでも変更された場合でも、完全に再現可能な結果を​​期待しないでください。これらの要因は、結果が超正確であると信頼すべきではないことを意味しますが、うまくいけば、それらは有用なほど十分に近いはずです.

これを読んだ後、ファイル名を変更すると、次のようになりました。

villar@localhost ~ $ mv a.out a.out2345345345
villar@localhost ~ $ cache=8 && valgrind --tool=cachegrind --I1=$((cache * 64)),$cache,64 --D1=$((cache * 64)),$cache,64 --L2=262144,4096,64 ./a.out2345345345 

==15022== D   refs:       40,652  (28,567 rd   + 12,085 wr)
==15022== D1  misses:     10,737  ( 8,201 rd   +  2,536 wr)
==15022== LLd misses:      1,517  ( 1,054 rd   +    463 wr)
==15022== D1  miss rate:    26.4% (  28.7%     +   20.9%  )
==15022== LLd miss rate:     3.7% (   3.6%     +    3.8%  )

名前を「a.out」に戻すと、以前とまったく同じ結果になりました。

ファイル名またはそのパスを変更すると、スタックのベースが変更されることに注意してください!!. エフゲニー氏が以前のコメントで言ったことを読んだ後、これが原因かもしれません

現在の作業ディレクトリを変更すると、対応する環境変数 (およびその長さ) も変更されます。通常、すべての環境変数のコピーはスタックのすぐ上に格納されるため、スタック変数の割り当てとキャッシュ ミスの数が異なります。(そして、シェルは「PWD」以外のいくつかの変数を変更できます)。

編集:ドキュメントにも次のように書かれています:

プログラムの起動/シャットダウンは、興味深いものではなく、出力を複雑にするだけの多くの関数を呼び出します。これらを何らかの形で除外するとよいでしょう。

シミュレートされたキャッシュは、プログラムの開始と終了を追跡している可能性があり、それがバリエーションの原因となっています。

于 2013-07-08T13:47:25.973 に答える