キャッシュは、CPUがメモリ要求が満たされるのを待機してストールする回数を減らし(メモリレイテンシを回避)、2番目の効果として、転送する必要のあるデータ全体の量を減らすためにあります(メモリ帯域幅)。
メモリフェッチレイテンシの影響を回避するための手法は、通常、最初に検討するべきものであり、時には長い道のりを助けます。限られたメモリ帯域幅は、特にマルチコアやマルチスレッドアプリケーションで多くのスレッドがメモリバスを使用したい場合の制限要因でもあります。後者の問題への対処には、さまざまなテクニックが役立ちます。
空間的な局所性を改善することは、キャッシュにマップされた後、各キャッシュラインが完全に使用されることを保証することを意味します。さまざまな標準ベンチマークを調べたところ、キャッシュラインが追い出される前に、それらの驚くべき大部分がフェッチされたキャッシュラインの100%を使用できないことがわかりました。
キャッシュラインの使用率を改善すると、3つの点で役立ちます。
- これは、より有効なデータをキャッシュに収める傾向があり、実質的に有効なキャッシュサイズが増加します。
- 同じキャッシュラインにより多くの有用なデータを収める傾向があり、要求されたデータがキャッシュで見つかる可能性が高くなります。
- フェッチ数が減るため、メモリ帯域幅の要件が軽減されます。
一般的な手法は次のとおりです。
- より小さいデータ型を使用する
- データを整理して位置合わせの穴を避けます(サイズを小さくして構造体メンバーをソートするのも1つの方法です)。
- 標準の動的メモリアロケータに注意してください。これにより、ウォームアップ時にホールが発生し、メモリ内のデータが分散する可能性があります。
- 隣接するすべてのデータが実際にホットループで使用されていることを確認してください。それ以外の場合は、データ構造をホットコンポーネントとコールドコンポーネントに分割して、ホットループがホットデータを使用することを検討してください。
- 不規則なアクセスパターンを示すアルゴリズムとデータ構造を避け、線形データ構造を優先します。
また、キャッシュを使用する以外にも、メモリの待ち時間を隠す方法があることに注意してください。
最近のCPUには、多くの場合、1つ以上のハードウェアプリフェッチャーがあります。彼らはキャッシュのミスを訓練し、規則性を見つけようとします。たとえば、後続のキャッシュラインを数回ミスした後、ハードウェアプリフェッチャーはキャッシュラインをキャッシュにフェッチし始め、アプリケーションのニーズを予測します。通常のアクセスパターンを使用している場合、ハードウェアプリフェッチャーは通常、非常に優れています。また、プログラムが通常のアクセスパターンを表示しない場合は、自分でプリフェッチ命令を追加することにより、状況を改善できます。
常にキャッシュでミスする命令が互いに近くに発生するような方法で命令を再グループ化すると、CPUがこれらのフェッチをオーバーラップして、アプリケーションが1つのレイテンシヒットしか維持できない場合があります(メモリレベルの並列処理)。
全体的なメモリバスの負荷を減らすには、いわゆる一時的な局所性に対処する必要があります。つまり、キャッシュから削除されていないデータを再利用する必要があります。
同じデータにアクセスするループをマージし(ループフュージョン)、タイル化またはブロック化と呼ばれる書き換え技術を使用して、これらの余分なメモリフェッチを回避しようとします。
この書き換え演習にはいくつかの経験則がありますが、プログラムのセマンティクスに影響を及ぼさないようにするには、通常、ループ搬送データの依存関係を慎重に検討する必要があります。
これらは、通常、2番目のスレッドを追加した後、スループットの向上の多くが見られないマルチコアの世界で実際に見返りがあるものです。