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