mttest のパフォーマンスデータが収集され、mttest.1.er と mttest.2.er の 2 つの実験がパフォーマンスアナライザの別々のインスタンスで開かれていることを確認してください。 これらの作業がまだ済んでいない場合には、mttest サンプルのデータ収集を参照して実施してください。
1 CPU 実験 mttest.2.er の場合、ComputeA() の包括ユーザー CPU 時間は ComputeB() の値とほぼ同じです。
4 CPU 実験 mttest.1.er の場合、ComputeB() は ComputeA() よりはるかに多い包括ユーザー CPU 時間を消費します。
以下の手順は、4 CPU 実験 mttest.1.er を対象としています。
両方のコードは同じで、 変数に 1 を加算するループです。 すべてのユーザー CPU 時間は、このループで消費されます。 ComputeB() が ComputeA() より多くの時間を使用する理由をつきとめるには、この 2 つの関数を呼び出すコードを調べる必要があります。
ComputeA() と ComputeB() は両方ともポインタを使用した参照によって呼び出されるので、これらの関数の名前はソースコードにありません。
cache_trash() が ComputeB() の呼び出し元であることを確認するには、「関数」タブで ComputeB() を選択して「呼び出し元-呼び出し先」タブをクリックします。
ComputeA() は、スレッドの作業ブロック内にある 1 つの倍精度浮動小数点型の値 (&array->list[0]) を引数として使用して呼び出されています。他のスレッドと競合することなく、この読み取りと書き込みを直接行うことができます。
一方 ComputeB() は、メモリー内で連続した語を占める一連の倍精度浮動小数点数型の値 (&element[array- >index]) を使用して呼び出されています。 あるスレッドがメモリー内にあるこれらのアドレスの 1 つに書き込みを行う場合、キャッシュ内にそのアドレスの内容を保持している他のスレッドは、必ずそのデータを削除しなければなりません。なぜなら、そのデータはすでに古くなっているからです。 後にスレッドの 1 つがプログラムでそのデータを必要とした場合は、そのデータ項目がまったく変更されていない場合でもメモリーからデータキャッシュにコピーし直さなければなりません。 この結果キャッシュに誤りが発生し、すなわちデータキャッシュにないデータにアクセスしようとし、大量の CPU 時間が無駄に消費されます。 4 CPU 実験において ComputeB() が ComputeA() よりはるかに多くのユーザー CPU 時間を使用するのは、このためです。
1 CPU 実験の場合、一度に実行しているスレッドは 1 つだけであるため、他のスレッドはメモリーに書き込めません。 実行中のスレッドのキャッシュデータが無効となることは決してありません。 キャッシュの誤りもメモリーからのコピーも発生しないので、利用できる CPU が 1 つしかない場合の ComputeB() のパフォーマンスは ComputeA() と同じように効率的です。
ハードウェアカウンタをもつコンピュータを使用している場合には、4 CPU 実験を再度実行し、いずれか 1 つのキャッシュハードウェアカウンタ (たとえばキャッシュの誤りやストールサイクルの) のデータを収集します。 UltraSPARC?III ハードウェアでは、次のコマンドを使用できます。
% collect -p off -h dcstall -o mttest.3.er mttest
「ファイル」 「追加」を選択し、この実験の結果を以前の 4 CPU 実験と結合します。 ComputeA と ComputeB のハードウェアカウンタデータを「関数」タブと「ソース」タブで確認します。