学校の課題として、構成ファイルを読み取ったりAPI呼び出しを使用したりせずに、L1データキャッシュのラインサイズを取得する方法を見つける必要があります。この情報を分析して取得するために、メモリアクセスの読み取り/書き込みタイミングを使用するとします。では、どうすればよいでしょうか。
割り当ての別の部分の不完全な試行で、キャッシュのレベルとサイズを見つけるには、次のようにします。
for (i = 0; i < steps; i++) {
arr[(i * 4) & lengthMod]++;
}
たぶん、2行目の(i * 4)
パートを変えるだけでいいのではないかと思っていました。キャッシュラインのサイズを超えたら、それを置き換える必要があるかもしれません。しかし、それはとても簡単ですか?必要なブロックはすでにメモリのどこかにある可能性がありますか?または、私がまだ十分な大きさのsteps
を持っている場合でも、非常に正確に機能するという事実を信頼できますか?
[〜#〜]更新[〜#〜]
GitHubでの試みを示します...以下の主要部分
// repeatedly access/modify data, varying the STRIDE
for (int s = 4; s <= MAX_STRIDE/sizeof(int); s*=2) {
start = wall_clock_time();
for (unsigned int k = 0; k < REPS; k++) {
data[(k * s) & lengthMod]++;
}
end = wall_clock_time();
timeTaken = ((float)(end - start))/1000000000;
printf("%d, %1.2f \n", s * sizeof(int), timeTaken);
}
問題は、タイミングの違いがあまりないようです。ご参考までに。 L1キャッシュ以降。 SIZE = 32 K(配列のサイズ)
BIG char
配列を割り当てます(L1キャッシュに収まりきらないほど大きいことを確認してくださいまたは L2キャッシュ)。ランダムデータを入力します。
n
バイトのステップで配列の上を歩き始めます。取得したバイトを合計するなど、何かを行います。
n
のさまざまな値で1バイトから何バイト/秒まで処理できるかをベンチマークして計算します。1から始まり、1000程度までカウントします。ベンチマークが計算された合計を出力することを確認してください。そうすることで、コンパイラーがベンチマークされたコードを最適化できなくなる可能性があります。
n
==キャッシュラインサイズの場合、各アクセスは、新しいラインをL1キャッシュに読み込む必要があります。したがって、ベンチマークの結果はその時点でかなり急激に遅くなるはずです。
配列が十分に大きい場合は、最後に到達するまでに、配列の先頭のデータはすでにキャッシュからなくなっています。これは必要なことです。したがって、n
をインクリメントして再度開始した後は、必要なデータがすでにキャッシュにあることによる結果への影響はありません。
Calibrator を見てください。すべての著作物は著作権で保護されていますが、 ソースコード は自由に利用できます。その ドキュメント から、キャッシュラインサイズを計算するという考えは、すでにここで述べたことよりもはるかに教育されているように思えます。
私たちのキャリブレーターツールの根底にある考え方は、パフォーマンスがマイクロベンチマークで、発生するキャッシュミスの頻度にのみ依存することです。私たちのキャリブレーターは単純なCプログラムであり、主に100万回のメモリ読み取りを実行する小さなループです。ストライド(2つの後続のメモリアクセス間のオフセット)とメモリ領域のサイズを変更することで、キャッシュミス率を変化させます。
原則として、キャッシュミスの発生は配列サイズによって決まります。 L1キャッシュに収まる配列サイズでは、データがキャッシュに読み込まれると、キャッシュミスは発生しません。同様に、L1キャッシュサイズを超えているがL2に収まる配列では、L1ミスは発生しますが、L2ミスは発生しません。最後に、L2より大きい配列は、L1とL2の両方のミスを引き起こします。
キャッシュミスの頻度は、アクセスストライドとキャッシュラインサイズによって異なります。ストライドがキャッシュラインサイズ以上の場合、反復ごとにキャッシュミスが発生します。ストライドがキャッシュラインサイズよりも小さい場合、キャッシュミスはn回の繰り返し(平均)ごとにのみ発生します。nはキャッシュラインサイズ/ストライドの比率です。
したがって、ミスのない実行時間と反復ごとに1回のミスがある実行時間を比較することで、キャッシュミスのレイテンシを計算できます。 このアプローチが機能するのは、メモリアクセスが純粋にシーケンシャルに実行される場合のみです。つまり、2つ以上のロード命令もメモリアクセスも純粋なCPU作業もオーバーラップできないようにする必要があります。これを達成するために、単純なポインター追跡メカニズムを使用します。アクセスするメモリ領域は、各ロードが次の反復での後続のロードのアドレスを返すように初期化されます。したがって、スーパースカラーCPUは、投機的実行によってメモリアクセスのレイテンシを隠す能力から恩恵を受けることはできません。
キャッシュ特性を測定するために、ストライドと配列サイズを変えて、実験を数回実行しました。ストライドが少なくとも4バイトから予想される最大キャッシュラインサイズの2倍の間で変化し、配列サイズが予想される最小キャッシュサイズの半分から予想される最大キャッシュサイズの少なくとも10倍まで変化することを確認します。
コメントアウトしなければならなかった#include "math.h"
それをコンパイルするために、その後私のラップトップのキャッシュ値を正しく見つけました。生成されたポストスクリプトファイルも表示できませんでした。
アセンブラーでCPUID
関数を使用できますが、移植性はありませんが、必要な機能が得られます。
Intelマイクロプロセッサの場合、cpuid関数0x1を呼び出した後、bhに8を掛けることにより、キャッシュラインサイズを計算できます。
AMDマイクロプロセッサの場合、cpuid関数0x80000005を呼び出した後、データキャッシュラインサイズはclにあり、命令キャッシュラインサイズはdlにあります。
私はこれからこれを取り出しました 記事はこちら 。
私はあなたがプログラムを書くべきだと思います、それは現代のプロセスがハードウェアのプリフェッチをするので、配列をまっすぐではなくランダムな順序でウォークします。たとえば、次のセルの番号となるintの配列を作成します。私は1年前に同様のプログラムを行いました http://Pastebin.com/9mFScs9Z 私の英語で申し訳ありませんが、私はネイティブスピーカーではありません。
Memtest86の実装方法をご覧ください。彼らは何らかの方法でデータ転送速度を測定して分析します。レート変更のポイントは、L1、L2、および可能なL3キャッシュサイズに対応しています。
泥に引っかかって抜けられない場合は、 こちら をご覧ください。
あなたが求めていることを行う方法を説明するマニュアルとコードがあります。コードもかなり高品質です。 「サブルーチンライブラリ」をご覧ください。
コードとマニュアルはX86プロセッサに基づいています。
ある程度のメモリを使用する操作の時間を計るにはそれで十分だと思います。次に、操作で使用されるメモリ(たとえば、オペランド)を徐々に増やします。操作のパフォーマンスが大幅に低下すると、制限が見つかります。
私はそれらを印刷せずに大量のバイトを読み取るだけにします(印刷するとパフォーマンスが低下し、ボトルネックになります)。読み取り中のタイミングは、データがL1に収まらなくなるまで、読み取られたバイト数に直接比例している必要があります。そうしないと、パフォーマンスに影響します。
また、プログラムの開始時と時間のカウントを開始する前に、メモリを一度割り当てる必要があります。
ただのメモ。
キャッシュラインサイズはいくつかのARM= Cortexファミリで可変であり、現在のプログラムへの通知なしで実行中に変更できます。