2

ここでブログ投稿を読み、同様のことを試みました。例1と2の内容を確認するコードは次のとおりです。

int doSomething(long numLoop,int cacheSize){
    long k;
    int arr[1000000];
    for(k=0;k<numLoop;k++){
        int i;
        for  (i = 0; i < 1000000; i+=cacheSize) arr[i] = arr[i];
    }
}

ブログ投稿で述べたように、doSomething(1000,2) と doSomething(1000,1) の実行時間はほぼ同じはずですが、それぞれ 2.1 秒と 4.3 秒でした。誰でも説明を手伝ってもらえますか?ありがとうございました。

更新 1: 配列のサイズを 100 倍に増やしました

int doSomething(long numLoop,int cacheSize){
    long k;
    int * buffer;
    buffer = (int*) malloc (100000000 * sizeof(int));
    for(k=0;k<numLoop;k++){
        int i;
        for  (i = 0; i < 100000000; i+=cacheSize) buffer[i] = buffer[i];
    }
}

残念ながら、doSomething(10,2) と doSomething(10,1) の実行時間は依然として大きく異なり、3.02 秒と 5.65 秒です。誰でもあなたのマシンでこれをテストできますか?

4

2 に答える 2

2

4Mのアレイサイズは十分な大きさではありません。配列全体がキャッシュに収まる(そして最初のkループの後にキャッシュに収まる)ので、タイミングは命令の実行によって支配されます。arrキャッシュサイズよりもはるかに大きくすると、期待される効果が見られるようになります。

arr(キャッシュより大きくすると、追加の効果が見られますarr。キャッシュを超えるまで、ランタイムはサイズに比例して増加するはずです。パフォーマンスが低下し、突然悪化し、新しい線形スケールでランタイムが増加します。 )。

編集:次の変更を加えて、2番目のバージョンを試しました。

  1. に変更して、最適化されないvolatile int *bufferようにします。buffer[i] = buffer[i]
  2. でコンパイルし-O2て、ループのオーバーヘッドが支配的になるのを防ぐためにループが十分に最適化されていることを確認します。

私がそれを試みるとき、私はほとんど同じ時間を得る:

kronos /tmp $ time ./dos 2
./dos 2  1.65s user 0.29s system 99% cpu 1.947 total
kronos /tmp $ time ./dos 1
./dos 1  1.68s user 0.25s system 99% cpu 1.926 total

ここでは、ストライドを2つの完全なキャッシュラインにすることの効果を確認できます。

kronos /tmp $ time ./dos 16
./dos 16  1.65s user 0.28s system 99% cpu 1.926 total
kronos /tmp $ time ./dos 32
./dos 32  1.06s user 0.30s system 99% cpu 1.356 total
于 2012-09-27T01:16:33.923 に答える
2

doSomething(1000, 2) を使用すると、doSomething(1000,1) を使用した場合の半分の回数の内部ループを実行しています。

内側のループは cacheSize だけインクリメントするため、値 2 では値 1 の半分の反復しか得られません。配列のサイズは、一般的な仮想メモリ ページ サイズである 4MB 前後です。

実際、変数の変更がないため、優れたコンパイラがこのループを最適化しないことに少し驚いています。それは問題の一部かもしれません。

于 2012-09-27T01:06:39.093 に答える