web-dev-qa-db-ja.com

L1キャッシュミスのコストはいくらですか?

Edit:参考のために(誰かがこの質問に出くわした場合)、Igor Ostrovskyは great post についてキャッシュミスについて書いています。いくつかの異なる問題について説明し、サンプル番号を示します。 編集の終了

いくつかのテストを行いました<long story goes here>そして、パフォーマンスの違いがメモリキャッシュミスによるものかどうか疑問に思っています。次のコードは問題を示しており、重要なタイミング部分に要約しています。次のコードには、メモリをランダムな順序で、次にアドレスの昇順にアクセスするループがいくつかあります。

XPマシン(VS2005でコンパイル:cl/O2)およびLinuxボックス(gcc –Os)で実行しました。どちらも同様の時間を生成しました。これらの時間はミリ秒単位です。ループは実行されており、最適化されていません(そうでない場合、「即座に」実行されます)。

 *** 20000ノードのテスト
注文時間合計:888.822899 
ランダム時間合計:2155.846268 

これらの数字は理にかなっていますか?違いは主にL1キャッシュミスによるものですか、それとも他に何かが起こっていますか? 20,000 ^ 2のメモリアクセスがあり、すべてがキャッシュミスであった場合、ミスごとに約3.2ナノ秒になります。テストしたXP(P4)マシンは3.2GHzであり、32KBのL1キャッシュと512KBのL2が疑わしい(しかし、わからない))20,000エントリ(80KB)があると思いますL2ミスの重要な数ではありません。したがって、これは(3.2*10^9 cycles/second) * 3.2*10^-9 seconds/miss) = 10.1 cycles/miss。それは私には高いようです。そうではないかもしれませんし、私の数学が悪いかもしれません。 VTuneでキャッシュミスを測定しようとしましたが、BSODがありました。そして今、ライセンスサーバー(grrrr)に接続することができません。

typedef struct stItem
{
   long     lData;
   //char     acPad[20];
} LIST_NODE;



#if defined( WIN32 )
void StartTimer( LONGLONG *pt1 )
{
   QueryPerformanceCounter( (LARGE_INTEGER*)pt1 );
}

void StopTimer( LONGLONG t1, double *pdMS )
{
   LONGLONG t2, llFreq;

   QueryPerformanceCounter( (LARGE_INTEGER*)&t2 );
   QueryPerformanceFrequency( (LARGE_INTEGER*)&llFreq );
   *pdMS = ((double)( t2 - t1 ) / (double)llFreq) * 1000.0;
}
#else
// doesn't need 64-bit integer in this case
void StartTimer( LONGLONG *pt1 )
{
   // Just use clock(), this test doesn't need higher resolution
   *pt1 = clock();
}

void StopTimer( LONGLONG t1, double *pdMS )
{
   LONGLONG t2 = clock();
   *pdMS = (double)( t2 - t1 ) / ( CLOCKS_PER_SEC / 1000 );
}
#endif



long longrand()
{
   #if defined( WIN32 )
   // Stupid cheesy way to make sure it is not just a 16-bit Rand value
   return ( Rand() << 16 ) | Rand();
   #else
   return Rand();
   #endif
}

// get random value in the given range
int randint( int m, int n )
{
   int ret = longrand() % ( n - m + 1 );
   return ret + m;
}

// I think I got this out of Programming Pearls (Bentley).
void ShuffleArray
(
   long *plShuffle,  // (O) return array of "randomly" ordered integers
   long lNumItems    // (I) length of array
)
{
   long i;
   long j;
   long t;

   for ( i = 0; i < lNumItems; i++ )
      plShuffle[i] = i;

   for ( i = 0; i < lNumItems; i++ )
      {
      j = randint( i, lNumItems - 1 );

      t = plShuffle[i];
      plShuffle[i] = plShuffle[j];
      plShuffle[j] = t;
      }
}



int main( int argc, char* argv[] )
{
   long          *plDataValues;
   LIST_NODE     *pstNodes;
   long          lNumItems = 20000;
   long          i, j;
   LONGLONG      t1;  // for timing
   double dms;

   if ( argc > 1 && atoi(argv[1]) > 0 )
      lNumItems = atoi( argv[1] );

   printf( "\n\n*** Testing %u nodes\n", lNumItems );

   srand( (unsigned int)time( 0 ));

   // allocate the nodes as one single chunk of memory
   pstNodes = (LIST_NODE*)malloc( lNumItems * sizeof( LIST_NODE ));
   assert( pstNodes != NULL );

   // Create an array that gives the access order for the nodes
   plDataValues = (long*)malloc( lNumItems * sizeof( long ));
   assert( plDataValues != NULL );

   // Access the data in order
   for ( i = 0; i < lNumItems; i++ )
      plDataValues[i] = i;

   StartTimer( &t1 );

   // Loop through and access the memory a bunch of times
   for ( j = 0; j < lNumItems; j++ )
      {
      for ( i = 0; i < lNumItems; i++ )
         {
         pstNodes[plDataValues[i]].lData = i * j;
         }
      }

   StopTimer( t1, &dms );
   printf( "Total Ordered Time: %f\n", dms );

   // now access the array positions in a "random" order
   ShuffleArray( plDataValues, lNumItems );

   StartTimer( &t1 );

   for ( j = 0; j < lNumItems; j++ )
      {
      for ( i = 0; i < lNumItems; i++ )
         {
         pstNodes[plDataValues[i]].lData = i * j;
         }
      }

   StopTimer( t1, &dms );
   printf( "Total Random Time: %f\n", dms );

}
68
Mark Wilkins

数値が意味をなすかどうかの答えを提供することはできませんが(キャッシュレイテンシに精通していませんが、レコード〜10サイクルでは、L1キャッシュミスは正しい音を出します)、提供できます Cachegrind は、2つのテスト間のキャッシュパフォーマンスの違いを実際に確認するのに役立つツールです。

Cachegrindは、キャッシュとブランチのヒット/ミスをプロファイリングするValgrindツール(常に使いやすいmemcheckを強化するフレームワーク)です。実際にプログラムで取得しているキャッシュヒット/ミスの数がわかります。

23
Falaina

これは、チョコレートチップクッキーのベーキングとの類推により、キャッシュミスの相対的なコストに関する洞察を提供する試みです...

あなたの手はあなたのレジスタです。生地にチョコレートチップスを落とすのに1秒かかります。

キッチンカウンターはあなたのL1キャッシュであり、レジスタよりも12倍遅いです。カウンターに足を踏み入れてバッグを拾うには12 x 1 = 12秒かかりますクルミの、そしてあなたの手にいくつかを空にします。

冷蔵庫はL2キャッシュで、L1の4倍の速度です。冷蔵庫まで歩いて、開いて、最後に移動するには4 x 12 = 48秒かかります夜の残り物を邪魔にならないように、卵のカートンを取り出し、カートンを開けて、カウンターに3個の卵を置き、カートンを冷蔵庫に戻します。

食器棚はL3キャッシュであり、L2の3倍遅くなります。食器棚まで3ステップかかるには3 x 48 = 2分24秒かかります。曲がって、ドアを開け、根を張ってベーキング用品の缶を見つけ、それを食器棚から取り出して、開けて、ベーキングパウダーを見つけて、カウンターに置いて、床にこぼした混乱を掃きます。

そしてメインメモリ?それは、L3の5倍遅いコーナーストアです。財布を見つけ、靴とジャケットを着て、通りをダッシュ​​し、1リットルをつかむには5 x 2:24 = 12分かかります牛乳を飲んで家に帰り、靴とジャケットを脱いでキッチンに戻ります。

allこれらのアクセスは一定の複雑さであることに注意してください-O(1)-しかし、それらの違いはhugeパフォーマンスへの影響。ビッグOの複雑さのために純粋に最適化することは、バッターにチョコレートチップを追加するか、一度に10を追加するかを決定するようなものです。あなたの食料品リストに載っています。

ストーリーのモラル:メモリアクセスを整理して、CPUができるだけ食料品を買わないようにします。

数値は CPU Cache Flushing Fallacy ブログ投稿から取得されました。これは、特定の2012年時代のIntelプロセッサについて、次のことが当てはまることを示しています。

  • レジスタアクセス=サイクルごとに4命令
  • L1レイテンシ= 3サイクル(12 xレジスタ)
  • L2レイテンシ= 12サイクル(4 x L1、48 xレジスタ)
  • L3レイテンシ= 38サイクル(3 x L2、12 x L1、144 xレジスタ)
  • DRAMレイテンシ= 65 ns = 3 GHz CPUで195サイクル(5 x L3、15 x L2、60 x L1、720 xレジスタ)

Gallery of Processor Cache Effects も、このトピックに関する良い読み物です。

Mmmm, cookies ...

58
yoyo

L1キャッシュミスの3.2nsは完全に妥当です。比較のために、ある特定の最新のマルチコアPowerPC CPUでは、L1ミスは約4サイクル-L2キャッシュからの距離に応じて、コアによっては他のコアよりも少し長くなります(そうです) 。 L2ミスは少なくとも6サイクルです。

キャッシュはパフォーマンスのすべてです。 CPUはメモリよりもはるかに高速であるため、コアではなくメモリバス用に実際にほぼ最適化されています。

17
Crashworks

ええ、それは主にL1キャッシュミスのように見えます。

L1キャッシュミスの10サイクルは、おそらく低域で少し妥当と思われます。

読み取りRAMは100秒のオーダーになるか、1000秒になる可能性があります(今は数学ができないので疲れています;))ので、まだ大きな勝利ですその上。

6
Goz

Cachegrindの使用を計画している場合は、キャッシュヒット/ミスシミュレータのみであることに注意してください。常に正確であるとは限りません。たとえば、あるメモリの場所、たとえばループ内で0x1234に1000回アクセスすると、cachegrindは、次のようなものがあっても、常に1つのキャッシュミス(最初のアクセス)しかなかったことを示します。

ループ内のclflush 0x1234。

X86では、これにより1000のキャッシュミスがすべて発生します。

3
user1046529

Lavalys Everestからの3.4GHz P4のいくつかの数値:

  • l1 dcacheは8K(キャッシュライン64バイト)
  • L2は512Kです
  • L1フェッチレイテンシは2サイクルです
  • L2フェッチレイテンシは、表示されているものの約2倍、20サイクルです。

詳細: http://www.freeweb.hu/instlatx64/GenuineIntel0000F25_P4_Gallatin_MemLatX86.txt

(待ち時間については、ページの下部をご覧ください)

2
terminus

多くのテストをせずに確実に何かを言うことは困難ですが、私の経験では、特にランダムアクセスのシナリオでは、その違いの規模は間違いなくCPU L1および/またはL2キャッシュに起因します。各アクセスが少なくとも最後からの最小距離であることを確認することで、おそらくさらに悪化する可能性があります。

0
Tim Sylvester