CPUキャッシュは、コンピュータの中央処理装置(CPU)がメインメモリからデータにアクセスする際の平均コスト(時間またはエネルギー)を削減するために使用するハードウェアキャッシュです。[ 1 ]キャッシュは、プロセッサコアに近い場所に配置された、より小さく高速なメモリであり、頻繁に使用されるメインメモリの場所からデータのコピーを格納し、アクセスに数十倍から数百倍の時間がかかる可能性のあるメインメモリを常に参照する必要性を回避します。
キャッシュメモリは通常、スタティックランダムアクセスメモリ(SRAM)で実装されますが、これは1ビットを格納するために複数のトランジスタを必要とします。そのため、占有面積の点でコストが高く、最新のCPUではキャッシュがチップ面積で最大の部分を占めるのが一般的です。キャッシュのサイズは、コスト削減のためにチップを小型化したいという一般的な要望とのバランスを取る必要があります。最新の設計の中には、物理的に小型のeDRAMを使用してキャッシュの一部または全部を実装しているものがあります。eDRAMはSRAMよりも使用速度は遅いものの、同じチップ面積でより大きなキャッシュ容量を実現できます。
ほとんどのCPUは、複数のキャッシュレベル(L1、L2、多くの場合L3、まれにL4)の階層構造を持ち、レベル1には命令固有のキャッシュ(Iキャッシュ)とデータ固有のキャッシュ(Dキャッシュ)が別々に存在します。 [ 2 ]異なるレベルはチップの異なる領域に実装されます。L1はCPUコアのできるだけ近くに配置されるため、信号経路が短くなり最高の速度が得られますが、慎重な設計が必要です。L2キャッシュはCPUとは物理的に分離されており、動作速度は遅くなりますが、チップ設計者への要求は少なく、CPU設計に影響を与えることなく大幅に大きくすることができます。L3キャッシュは通常、複数のCPUコア間で共有されます。
上記で述べた最も重要なキャッシュの「キャッシュサイズ」には含まれない、他の種類のキャッシュも存在します。例えば、ほとんどのCPUに搭載されているメモリ管理ユニット(MMU)の一部である変換ルックアサイドバッファ(TLB)などが挙げられます。入出力セクションにも、同様の目的を果たすデータバッファが含まれていることがよくあります。
メインメモリ内のデータにアクセスするには、複数のステップからなるプロセスが用いられ、各ステップで遅延が発生します。例えば、単純なコンピュータシステムでメモリから値を読み取る場合、CPUはまずアドレスバス上でアクセスするアドレスを宣言し、その値が安定するまで一定時間待機します。その値を保持するメモリデバイス(通常はDRAMで実装)は、CPUが直接読み取るには電力が不足している非常に低消費電力の形式でその値を保持しています。そのため、CPUはストレージからその値をデータバスに接続された小さなバッファにコピーする必要があります。そして、データバスから値を読み取る前に、この値が安定するまで一定時間待機します。
メモリをCPUに物理的に近づけることで、バスが安定するまでの時間が短縮されます。また、DRAMを、読み出し時に増幅を必要としない形式で値を保持するSRAMに置き換えることで、メモリ内部の遅延が解消されます。これにより、キャッシュの応答速度と読み書き速度が大幅に向上します。ただし、SRAMは種類にもよりますが、1ビットを保持するために4~6個のトランジスタを必要とします。一方、DRAMは一般的に1ビットあたり1個のトランジスタと1個のコンデンサを使用するため、同じチップ面積でより多くのデータを保存できます。
メモリの一部をより高速な形式で実装することで、パフォーマンスを大幅に向上させることができます。メモリ内の特定の場所への読み書きを行う際、プロセッサはその場所のデータが既にキャッシュに存在するかどうかを確認します。存在する場合は、プロセッサははるかに低速なメインメモリではなく、キャッシュから読み書きを行います。
現代のデスクトップ、サーバー、産業用CPUの多くは、少なくとも3つの独立したキャッシュレベル(L1、L2、L3)と、異なる種類のキャッシュを備えています。

CPU キャッシュの初期の例としては、 1960 年代のAtlas 2 [ 3 ]やIBM System/360 Model 85 [ 4 ] [ 5 ]が挙げられます。キャッシュを使用した最初の CPU は、キャッシュが 1 レベルのみでした。後のレベル 1 キャッシュとは異なり、L1d (データ用) と L1i (命令用) に分割されていませんでした。分割 L1 キャッシュは 1976 年にIBM 801 CPU [ 6 ] [ 7 ]で始まり、1980 年代後半に主流となり、1997 年に ARMv5TE で組み込み CPU 市場に参入しました。2015 年現在、1ドル以下のSoCでさえL1 キャッシュを分割しています。また、L2 キャッシュも備えており、より大規模なプロセッサでは L3 キャッシュも備えています。L2 キャッシュは通常分割されておらず、既に分割されている L1 キャッシュの共通リポジトリとして機能します。マルチコア プロセッサの各コアには専用の L1 キャッシュがあり、通常はコア間で共有されません。 L2 キャッシュおよびそれより下位のキャッシュは、コア間で共有される場合があります。L4 キャッシュは現在では一般的ではなく、通常は静的ランダムアクセスメモリ(SRAM) ではなく、別のダイまたはチップ上の動的ランダムアクセスメモリ(DRAM) です。例外として、 L1 までのすべてのレベルのキャッシュにeDRAM が使用される場合があります。歴史的には、L1 も別のダイ上にありましたが、ダイサイズが大きくなったことで、最後のレベルを除いて、他のキャッシュレベルとともに統合できるようになりました。キャッシュの各レベルは、下位レベルよりも小さく、高速になる傾向があります。[ 8 ]
キャッシュ (歴史的に RAM と同様) は、一般的に 2、4、8、16 などのKiBのべき乗でサイズが決められてきました。MiBサイズ (つまり、より大きな非 L1 の場合)になると、非常に早い段階でこのパターンは崩れ、サイズを倍増させるパラダイムに強制されることなく、より大きなキャッシュを許容できるようになりました。たとえば、 2008 年 4 月に3 MiB の L2 キャッシュを備えたIntel Core 2 Duo が 登場しました。L1 キャッシュについては、そのサイズが一般的にまだ KiB の小さな数であるため、これはずっと後に起こりました。ただし、2012 年のIBM zEC12 は例外で、 当時としては異例に大きな 96 KiB の L1 データ キャッシュを獲得しました。たとえば、IBM z13は 96 KiB の L1 命令キャッシュ (および 128 KiB の L1 データ キャッシュ) を持ち、[ 9 ] 2018 年のIntel Ice Lake ベースのプロセッサは 48 KiB の L1 データ キャッシュと 48 KiB の L1 命令キャッシュを持っています 。 2020年、一部のIntel Atom CPU(最大24コア)は、4.5 MiBと15 MiBのキャッシュサイズ(またはその倍数)を備えています。[ 10 ] [ 11 ]
データは、キャッシュラインまたはキャッシュブロックと呼ばれる固定サイズのブロック単位でメモリとキャッシュ間で転送されます。キャッシュラインがメモリからキャッシュにコピーされると、キャッシュエントリが作成されます。キャッシュエントリには、コピーされたデータと要求されたメモリ位置(タグと呼ばれる)が含まれます。
プロセッサがメモリ内の特定の場所を読み書きする必要がある場合、まずキャッシュ内で対応するエントリを確認します。キャッシュは、要求されたメモリ位置の内容が、そのアドレスを含む可能性のあるすべてのキャッシュラインに存在するかどうかを確認します。プロセッサがメモリ位置がキャッシュ内に存在することを発見した場合、キャッシュヒットが発生したことになります。しかし、プロセッサがキャッシュ内にメモリ位置を見つけられなかった場合、キャッシュミスが発生したことになります。キャッシュヒットの場合、プロセッサは直ちにキャッシュライン内のデータを読み書きします。キャッシュミスの場合、キャッシュは新しいエントリを割り当て、メインメモリからデータをコピーし、その後、キャッシュの内容から要求を満たします。
キャッシュミス時に新しいエントリのためのスペースを確保するため、キャッシュは既存のエントリのいずれかを削除する必要がある場合があります。削除するエントリを選択するために使用するヒューリスティックは、置換ポリシーと呼ばれます。置換ポリシーの根本的な問題は、既存のキャッシュエントリのうち、将来使用される可能性が最も低いエントリを予測する必要があることです。将来を予測することは一般的に難しいため、利用可能なさまざまな置換ポリシーの中から最適なものを選ぶ完璧な方法はありません。よく使われる置換ポリシーの1つであるLRU( Least-Rested Used)は、最も最近アクセスされていないエントリを置き換えます。
一部のメモリ範囲をキャッシュ不可としてマークすることで、再アクセス頻度の低いメモリ領域のキャッシュを回避し、パフォーマンスを向上させることができます。これにより、再利用されないデータをキャッシュにロードする際のオーバーヘッドを回避できます。また、状況によっては、キャッシュエントリを無効化またはロックすることも可能です。
データがキャッシュに書き込まれると、いずれかの時点でメインメモリにも書き込まれる必要があります。この書き込みのタイミングは、書き込みポリシーと呼ばれます。ライトスルーキャッシュでは、キャッシュへの書き込みごとにメインメモリへの書き込みが発生します。一方、ライトバックキャッシュまたはコピーバックキャッシュでは、書き込みはすぐにメインメモリに反映されず、上書きされた場所はダーティとしてマークされ、キャッシュから追い出されたときにのみメインメモリに書き戻されます。このため、ライトバックキャッシュでの読み取りミスは、処理に2回のメモリアクセスが必要になる場合があります。1回目はダーティな場所をメインメモリに書き込むため、2回目はメモリから新しい場所を読み取るためです。また、ライトバックキャッシュにまだマッピングされていないメインメモリの場所への書き込みは、既にダーティになっている場所を追い出し、新しいメモリ場所のためにそのキャッシュ領域を解放する可能性があります。
中間的なポリシーも存在します。キャッシュはライトスルー方式ですが、書き込みデータは一時的にストアデータキューに保持される場合があります。これは通常、複数のストア処理をまとめて行うためであり、バスのターンアラウンド時間を短縮し、バスの利用率を向上させることができます。
メインメモリからキャッシュされたデータは、他のエンティティ(例えば、ダイレクトメモリアクセス(DMA)を使用する周辺機器や、マルチコアプロセッサの別のコアなど)によって変更される場合があり、その場合、キャッシュ内のコピーが古くなったり、陳腐化したりする可能性があります。また、マルチプロセッサシステムにおいてCPUがキャッシュ内のデータを更新すると、他のCPUに関連付けられたキャッシュ内のデータコピーが陳腐化します。データの一貫性を維持するためのキャッシュマネージャ間の通信プロトコルは、キャッシュコヒーレンスプロトコルと呼ばれます。
近年、メモリ性能とプロセッサ性能の速度差が指数関数的に拡大しているため、キャッシュ性能の測定が重要になってきています。キャッシュはこの速度差を縮小するために導入されました。そのため、特に高性能システムにおいては、キャッシュがプロセッサとメモリの速度差をどれだけ効果的に埋めることができるかを知ることが重要になります。キャッシュヒット率とキャッシュミス率は、この性能を決定する上で重要な役割を果たします。キャッシュ性能を向上させるには、ミス率を低減することが他の対策の中でも特に重要です。キャッシュへのアクセス時間を短縮することも、キャッシュ性能の向上につながり、最適化にも役立ちます。
メモリからキャッシュラインを1つフェッチするのにかかる時間(キャッシュミスによる読み出しレイテンシ)は、CPUがキャッシュラインを待っている間に処理すべきタスクがなくなるため、重要です。CPUがこの状態になると、ストールと呼ばれます。CPUはメインメモリに比べて高速化しているため、キャッシュミスによるストールによって、より多くの潜在的な計算処理が妨げられます。現代のCPUは、メインメモリからキャッシュラインを1つフェッチする間に、数百もの命令を実行できます。
この間、CPUを常に稼働させておくために、さまざまな手法が用いられてきました。その一つがアウトオブオーダー実行で、CPUはキャッシュミスデータを待っている命令の後に、独立した命令を実行しようと試みます。また、多くのプロセッサで採用されているもう一つの技術は同時マルチスレッド(SMT)です。これは、最初のスレッドが必要なCPUリソースが利用可能になるのを待っている間に、別のスレッドがCPUコアを使用できるようにするものです。

配置ポリシーは、メインメモリの特定のエントリのコピーがキャッシュのどこに配置されるかを決定します。配置ポリシーがキャッシュ内の任意のエントリを選択してコピーを保持できる場合、そのキャッシュは完全連想型と呼ばれます。もう一方の極端な例として、メインメモリの各エントリがキャッシュ内の 1 つの場所にのみ配置できる場合、そのキャッシュは直接マップ型です。多くのキャッシュは、メインメモリの各エントリがキャッシュ内の N 個の場所のいずれかに配置されるという妥協案を実装しており、N ウェイ セット アソシアティブと呼ばれます。[ 12 ]例えば、 AMD Athlonのレベル 1 データ キャッシュは 2 ウェイ セット アソシアティブであり、これはメインメモリの特定の場所がレベル 1 データ キャッシュ内の 2 つの場所のいずれかにキャッシュできることを意味します。
適切なアソシアティビティ値を選択するにはトレードオフが伴います。配置ポリシーによってメモリ位置をマッピングできる場所が10箇所ある場合、その位置がキャッシュにあるかどうかを確認するには、10個のキャッシュエントリを検索する必要があります。チェックする場所が増えると、消費電力とチップ面積が増え、時間も長くなる可能性があります。一方、アソシアティビティが高いキャッシュではミスが少なくなるため(競合ミスを参照)、CPUは低速なメインメモリからの読み出しに費やす時間を少なくすることができます。一般的なガイドラインとしては、アソシアティビティをダイレクトマップから2ウェイ、または2ウェイから4ウェイに倍増すると、ヒット率の向上効果はキャッシュサイズを倍増するのとほぼ同じです。ただし、アソシアティビティを4以上に増やしてもヒット率はそれほど向上せず[ 13 ]、通常は他の理由で行われます(仮想エイリアシングを参照)。一部のCPUは、低電力状態でキャッシュのアソシアティビティを動的に減らすことができ、これは省電力対策として機能します[ 14 ] 。
単純だが劣る順から、複雑だが優れている順に:
このキャッシュ構成では、メインメモリの各場所はキャッシュのエントリに 1 つだけ格納できます。そのため、ダイレクトマップ キャッシュは「一方向セット アソシアティブ」キャッシュとも呼ばれます。どのキャッシュ エントリの内容を追い出すかを選択できないため、配置ポリシーというものは存在しません。つまり、2 つの場所が同じエントリにマッピングされている場合、互いに追い出し合う可能性があります。ダイレクトマップ キャッシュは単純ですが、同等のパフォーマンスを得るにはアソシアティブ キャッシュよりもはるかに大きくする必要があり、予測不可能です。x をキャッシュ内のブロック番号、y をメモリのブロック番号、nをキャッシュ内のブロック数とすると、マッピングは方程式x = y mod nを使用して行われます。
メインメモリの各位置をキャッシュ内の2つの位置のいずれかにキャッシュできる場合、論理的な疑問は、どちらの位置を使用するかということです。最も単純で一般的に使用されている方式は、上の右側の図に示すように、メモリ位置のインデックスの最下位ビットをキャッシュメモリのインデックスとして使用し、各インデックスに対して2つのエントリを用意することです。この方式の利点の1つは、キャッシュに格納されるタグに、キャッシュメモリのインデックスによって示されるメインメモリアドレスの一部を含める必要がないことです。キャッシュタグのビット数が少ないため、必要なトランジスタ数が少なく、プロセッサ回路基板またはマイクロプロセッサチップ上のスペースを節約でき、読み取りと比較が高速になります。また、各ペアに対して1ビットだけを格納すればよいため、LRUアルゴリズムは特に単純です。
ダイレクトマップキャッシュの利点の1つは、シンプルかつ高速な投機実行が可能であることです。アドレスが計算されると、メモリ内のその場所のコピーが存在する可能性のあるキャッシュインデックスが1つ特定されます。そのキャッシュエントリを読み取ることができ、プロセッサはタグが要求されたアドレスと実際に一致するかどうかのチェックが完了する前に、そのデータを使って処理を続けることができます。
タグの一致が完了する前にプロセッサがキャッシュされたデータを使用するという考え方は、連想キャッシュにも適用できます。ヒントと呼ばれるタグのサブセットを使用して、要求されたアドレスにマッピングされる可能性のあるキャッシュエントリのうち、1つだけを選択できます。ヒントによって選択されたエントリは、完全なタグのチェックと並行して使用できます。ヒントの手法は、後述するように、アドレス変換のコンテキストで使用すると最も効果的です。
スキューキャッシュ[ 15 ]などの他の方式も提案されている。スキューキャッシュでは、ウェイ0のインデックスは上記のように直接だが、ウェイ1のインデックスはハッシュ関数で生成される。優れたハッシュ関数は、直接マッピングと競合するアドレスがハッシュ関数でマッピングされたときに競合しないという特性を持ち、そのため、病的なアクセスパターンによってプログラムが予期せず多数の競合ミスに悩まされる可能性が低くなる。欠点は、ハッシュ関数の計算による余分なレイテンシである。[ 16 ]さらに、新しいラインをロードして古いラインを追い出すとき、新しいラインが各ウェイの異なるインデックスのデータと競合するため、どの既存のラインが最も最近使用されていないかを判断するのが難しい場合がある。非スキューキャッシュのLRUトラッキングは通常、セット単位で行われる。それでも、スキューアソシアティブキャッシュは、従来のセットアソシアティブキャッシュに比べて大きな利点がある。[ 17 ]
真のセットアソシアティブキャッシュは、コンテンツアドレス指定可能なメモリなどを用いて、考えられるすべての方法を同時にテストします。擬似アソシアティブキャッシュは、考えられる各方法を一度に1つずつテストします。ハッシュリハッシュキャッシュとカラムアソシアティブキャッシュは、擬似アソシアティブキャッシュの例です。
最初にテストした方法でヒットが見つかった一般的なケースでは、擬似連想キャッシュは直接マップキャッシュと同じくらい高速ですが、直接マップキャッシュよりも競合ミス率がはるかに低く、完全連想キャッシュのミス率に近くなります。[ 16 ]
直接マップされたキャッシュと比較すると、セットアソシアティブキャッシュは、キャッシュセットにマッピングされるキャッシュセットインデックスのビット数が少なく、複数のウェイまたはブロックが残ります。たとえば、2ウェイセットアソシアティブキャッシュでは2ブロック、4ウェイセットアソシアティブキャッシュでは4ブロックです。直接マップされたキャッシュと比較すると、未使用のキャッシュインデックスビットはタグビットの一部になります。たとえば、2ウェイセットアソシアティブキャッシュはタグに1ビットを提供し、4ウェイセットアソシアティブキャッシュはタグに2ビットを提供します。マルチカラムキャッシュ[ 18 ]の基本的な考え方は、従来のセットアソシアティブキャッシュと同様にセットインデックスを使用してキャッシュセットにマッピングし、追加されたタグビットを使用してセット内のウェイをインデックスすることです。たとえば、4ウェイセットアソシアティブキャッシュでは、2ビットはそれぞれウェイ00、ウェイ01、ウェイ10、ウェイ11をインデックスするために使用されます。この二重キャッシュインデックスは「メジャーロケーションマッピング」と呼ばれ、そのレイテンシはダイレクトマップアクセスと同等です。マルチカラムキャッシュ設計[ 18 ]の広範な実験では、メジャーロケーションへのヒット率が90%にも達することが示されています。キャッシュマッピングがメジャーロケーションのキャッシュブロックと競合する場合、既存のキャッシュブロックは同じセット内の別のキャッシュウェイに移動され、これを「選択ロケーション」と呼びます。新しくインデックス付けされたキャッシュブロックは最近使用された(MRU)ブロックであるため、時間的局所性を考慮してマルチカラムキャッシュのメジャーロケーションに配置されます。マルチカラムキャッシュは高い連想性を持つキャッシュ用に設計されているため、各セットのウェイ数が多く、セット内の選択ロケーションを簡単に見つけることができます。キャッシュブロック内のメジャーロケーションについては、追加のハードウェアによって選択ロケーションインデックスが維持されます。
マルチカラムキャッシュは、高い連想性により高いヒット率を維持し、主要な場所におけるヒット率が高いため、ダイレクトマップキャッシュと同等の低レイテンシを実現しています。マルチカラムキャッシュにおける主要な場所と選択された場所の概念は、ARM Cortex Rチップ[ 19 ] 、 Intelのウェイ予測キャッシュメモリ[ 20 ] 、 IBMの再構成可能なマルチウェイ連想キャッシュメモリ[ 21 ]、およびOracleのアドレスタブビットに基づく動的キャッシュ置換ウェイ選択[ 22 ]など、いくつかのキャッシュ設計で使用されています。
キャッシュ行エントリは通常、以下の構造を持ちます。
データブロック(キャッシュライン)には、メインメモリからフェッチされた実際のデータが格納されます。タグには、メインメモリからフェッチされた実際のデータのアドレス(の一部)が格納されます。フラグビットについては後述します。
キャッシュの「サイズ」とは、キャッシュが保持できるメインメモリデータの量のことです。このサイズは、各データブロックに格納されるバイト数に、キャッシュに格納されるブロック数を掛けることで計算できます。(タグ、フラグ、エラー訂正コードビットはサイズには含まれませんが[ 23 ]、キャッシュの物理領域には影響します。)
キャッシュライン(メモリブロック)に付随する実効メモリアドレスは、(MSBからLSBまで)タグ、インデックス、ブロックオフセットに分割されます。[ 8 ] [ 24 ]
インデックスは、データがどのキャッシュセットに格納されているかを示します。インデックスの長さはsキャッシュセット用のビット。
ブロックオフセットは、キャッシュ行内の格納データブロック内の目的のデータを指定します。通常、実効アドレスはバイト単位なので、ブロックオフセットの長さはビット。ここで、bはデータブロックあたりのバイト数です。タグにはアドレスの最上位ビットが含まれており、現在のセット(インデックスによって取得されたセット)のすべての行に対してチェックされ、このセットに要求されたアドレスが含まれているかどうかを確認します。含まれている場合は、キャッシュヒットとなります。タグの長さ(ビット単位)は次のとおりです。
tag_length = address_length - index_length - block_offset_lengthブロックオフセットを単に「オフセット」[ 25 ]または「変位」[ 26 ] [ 27 ]と呼ぶ著者もいる。
オリジナルのPentium 4プロセッサは、8 KiBサイズの 4 ウェイ セット アソシアティブ L1 データ キャッシュを備えており、キャッシュ ブロックは 64 バイトでした。したがって、キャッシュ ブロックは 8 KiB / 64 = 128 個あります。セットの数は、キャッシュ ブロックの数をアソシアティビティのウェイの数で割った値に等しく、128 / 4 = 32 セットとなり、2 5 = 32 種類のインデックスが存在します。オフセットは 2 6 = 64通りあります。CPU アドレスは 32 ビット幅であるため、タグ フィールドには32 − 5 − 6 = 21 ビットが使用されることになります。
オリジナルの Pentium 4 プロセッサには、128 バイトのキャッシュ ブロックを持つ 256 KiB の 8 ウェイ セット アソシアティブ L2 統合キャッシュも搭載されていました。これは、タグ フィールドが32 − 8 − 7 = 17 ビット であることを意味します。 [ 25 ]
命令キャッシュは、キャッシュ行エントリごとに1つのフラグビット(有効ビット)のみを必要とします。有効ビットは、キャッシュブロックに有効なデータがロードされているかどうかを示します。
電源投入時、ハードウェアはすべてのキャッシュ内の有効なビットをすべて「無効」に設定します。システムによっては、マルチマスターバススヌーピングハードウェアが、あるプロセッサのキャッシュ内で他のプロセッサからブロードキャストされたアドレスを受信し、ローカルキャッシュ内の特定のデータブロックが古くなっていることを認識して無効としてマークする必要がある場合など、他のタイミングで有効なビットを「無効」に設定することもあります。
データキャッシュは通常、キャッシュラインごとに2つのフラグビット(有効ビットとダーティビット)を必要とします。ダーティビットがセットされているということは、メインメモリから読み出された後、関連するキャッシュラインが変更された(「ダーティ」)ことを示します。つまり、プロセッサがそのラインにデータを書き込みましたが、新しい値がメインメモリまで完全に伝播していないことを意味します。
キャッシュミスとは、キャッシュ内のデータの読み書きに失敗した状態を指し、その結果、メインメモリへのアクセスが大幅に遅延します。キャッシュミスには、命令読み取りミス、データ読み取りミス、データ書き込みミスの3種類があります。
命令キャッシュからのキャッシュ読み出しミスは、プロセッサ、または少なくとも実行スレッドが、命令がメインメモリからフェッチされるまで待機(停止)する必要があるため、一般的に最も大きな遅延を引き起こします。データキャッシュからのキャッシュ読み出しミスは、キャッシュ読み出しに依存しない命令を発行して、データがメインメモリから返されるまで実行を継続でき、依存する命令も実行を再開できるため、通常はより小さな遅延を引き起こします。データキャッシュへのキャッシュ書き込みミスは、書き込みをキューに入れることができ、後続の命令の実行にほとんど制限がないため、一般的に最も短い遅延を引き起こします。プロセッサは、キューがいっぱいになるまで実行を継続できます。ミスの種類の詳細については、「キャッシュのパフォーマンス測定とメトリック」を参照してください。
ほとんどの汎用CPUは、何らかの仮想メモリを実装しています。要約すると、マシン上で実行される各プログラムは、そのプログラム専用のコードとデータを含む独自の簡略化されたアドレス空間を参照するか、すべてのプログラムが共通の仮想アドレス空間で実行されるかのいずれかです。プログラムは、物理アドレス空間のアドレスではなく、仮想アドレス空間のアドレスに対して計算、比較、読み書きを行うことで実行されるため、プログラムが簡素化され、記述が容易になります。
仮想メモリでは、プロセッサがプログラムによって生成された仮想アドレスをメインメモリ内の物理アドレスに変換する必要があります。この変換を行うプロセッサの部分は、メモリ管理ユニット(MMU)と呼ばれます。MMUを経由する高速パスでは、オペレーティングシステムのページテーブル、セグメントテーブル、またはその両方からのマッピングをキャッシュした変換ルックアサイドバッファ(TLB)に格納された変換を実行できます。
本稿の議論においては、住所翻訳には3つの重要な特徴がある。
初期の仮想メモリシステムであるIBM M44/44Xでは、メインメモリへのプログラムによるアクセスの前に、コアメモリに保持されているマッピングテーブルへのアクセスが必要でした。 [ 28 ] [ NB 1 ]キャッシュがなく、マッピングテーブルメモリがメインメモリと同じ速度で動作していたため、メモリアクセスの速度は実質的に半分に低下しました。メインメモリ内のページテーブルをマッピングに使用した初期のマシンであるIBM System/360 Model 67とGE 645は、どちらもメモリ内のページテーブルへのアクセス用のキャッシュとして小さな連想メモリを備えていました。これらのマシンはどちらも、メインメモリ用のキャッシュを備えた最初のマシンであるIBM System/360 Model 85より前に登場したため、コンピュータシステムで使用された最初のハードウェアキャッシュは、データキャッシュや命令キャッシュではなく、TLB でした。
キャッシュは、インデックスまたはタグが物理アドレスに対応するか仮想アドレスに対応するかに基づいて、4つのタイプに分類できます。
この再帰の速度(ロードレイテンシ)はCPUのパフォーマンスにとって非常に重要であるため、最新のレベル1キャッシュのほとんどは仮想インデックス化されており、これにより少なくともMMUのTLBルックアップがキャッシュRAMからのデータ取得と並行して進行することが可能になります。
しかし、仮想インデックス方式はすべてのキャッシュレベルにとって最適な選択肢ではありません。仮想エイリアスの処理コストはキャッシュサイズが大きくなるにつれて増加するため、レベル2以上のキャッシュのほとんどは物理インデックス方式を採用しています。
キャッシュは従来、キャッシュタグに仮想アドレスと物理アドレスの両方を使用してきましたが、現在では仮想タグの使用は稀です。TLBルックアップがキャッシュRAMルックアップよりも先に完了すれば、タグ比較に間に合うように物理アドレスが利用可能になるため、仮想タグは不要になります。そのため、大規模なキャッシュは物理的にタグ付けされる傾向があり、仮想タグが使用されるのは、レイテンシが非常に低い小規模なキャッシュに限られます。近年の汎用CPUでは、仮想タグは後述する仮想ヒントに置き換えられています。
仮想インデックスとタグ付けに依存するキャッシュは、同じ仮想アドレスが異なる物理アドレスにマッピングされると(同名異義語)、一貫性がなくなります。これは、タグ付けに物理アドレスを使用するか、アドレス空間識別子をキャッシュラインに格納することで解決できます。ただし、後者のアプローチでは、複数のキャッシュラインが同じ物理アドレスのデータを格納することになる同名異義語の問題には対処できません。このような場所に書き込むと、キャッシュ内の 1 つの場所のみが更新され、他の場所には一貫性のないデータが残る可能性があります。この問題は、異なるアドレス空間に対して重複しないメモリレイアウトを使用することで解決できます。そうでない場合は、マッピングが変更されたときにキャッシュ(またはその一部)をフラッシュする必要があります。[ 34 ]
仮想タグの大きな利点は、連想キャッシュの場合、仮想アドレスから物理アドレスへの変換が行われる前にタグの一致処理を進めることができる点です。しかし、コヒーレンスプローブや削除処理では、処理対象の物理アドレスが提示されます。ハードウェアは、物理アドレスをキャッシュインデックスに変換する何らかの手段を備えている必要があり、通常は仮想タグだけでなく物理タグも格納します。比較のために述べると、物理タグ付きキャッシュは仮想タグを保持する必要がないため、よりシンプルです。TLBから仮想アドレスから物理アドレスへのマッピングが削除されると、その仮想アドレスを持つキャッシュエントリは、何らかの方法でフラッシュする必要があります。あるいは、TLBによってマッピングされていないページにキャッシュエントリが許可されている場合、ページテーブルでそれらのページのアクセス権が変更されると、それらのエントリはフラッシュする必要があります。
オペレーティングシステムは、仮想エイリアスがキャッシュ内に同時に存在しないことを保証することも可能です。オペレーティングシステムは、後述するページカラーリングを強制することでこの保証を実現します。初期のRISCプロセッサ(SPARC、RS/6000など)の中には、この方式を採用していたものもありました。しかし、仮想エイリアスの検出と削除にかかるハードウェアコストが低下し、完全なページカラーリングによるソフトウェアの複雑さとパフォーマンスの低下が増大したため、近年ではこの方式は使用されなくなりました。
連想キャッシュにおけるタグの2つの機能を区別することは有用である。タグはエントリセットのどの方向を選択するかを決定するために使用され、キャッシュがヒットしたかミスしたかを決定するために使用される。2番目の機能は常に正しくなければならないが、1番目の機能は推測に基づいて、時折間違った結果を出すことが許容される。
一部のプロセッサ(初期のSPARCなど)は、仮想タグと物理タグの両方を持つキャッシュを備えています。仮想タグはウェイ選択に使用され、物理タグはヒットかミスかを判定するために使用されます。このタイプのキャッシュは、仮想タグ付きキャッシュの低遅延性と、物理タグ付きキャッシュのシンプルなソフトウェアインターフェースという利点を享受できます。ただし、タグが重複するというコストが加わります。また、ミス処理時には、インデックス付けされたキャッシュラインの代替ウェイをプローブして仮想エイリアスを探し、一致するものがあれば削除する必要があります。
仮想タグの代わりに仮想ヒントを各キャッシュエントリに保持することで、余分な領域(および若干の遅延)を軽減できます。これらのヒントは仮想タグのサブセットまたはハッシュであり、キャッシュからデータと物理タグを取得する方法を選択するために使用されます。仮想タグ付きキャッシュと同様に、仮想ヒントが一致しても物理タグが一致しない場合があり、その場合は一致するヒントを持つキャッシュエントリを削除して、このアドレスのキャッシュが満たされた後のキャッシュアクセスで一致するヒントが1つだけになるようにする必要があります。仮想ヒントは仮想タグよりもビット数が少ないため、仮想ヒント付きキャッシュは仮想タグ付きキャッシュよりも競合ミスが多くなります。
仮想ヒントの究極の削減は、Pentium 4(WillametteおよびNorthwoodコア)に見られるかもしれません。これらのプロセッサでは、仮想ヒントは実質的に2ビットであり、キャッシュは4ウェイセットアソシアティブです。事実上、ハードウェアは仮想アドレスからキャッシュインデックスへの単純な順列を維持するため、フェッチされた4つの方法のうち適切なものを選択するためにコンテンツアドレス指定可能メモリ(CAM)は必要ありません。
大規模な物理インデックス付きキャッシュ(通常はセカンダリキャッシュ)には問題があります。それは、キャッシュ内でどのページが衝突するかをアプリケーションではなくオペレーティングシステムが制御するためです。プログラム実行ごとにページ割り当てが異なると、キャッシュの衝突パターンも異なり、プログラムのパフォーマンスに大きな差が生じる可能性があります。このような違いがあると、ベンチマーク実行で一貫性のある再現可能なタイミングを得ることが非常に困難になります。
この問題を理解するために、1 MiB の物理的にインデックス付けされたダイレクトマップ方式のレベル 2 キャッシュと 4 KiB の仮想メモリ ページを持つ CPU を考えてみましょう 。連続する物理ページは、256 ページを超えるとパターンが一周するまで、キャッシュ内の連続する場所にマッピングされます。各物理ページには、キャッシュ内のどこに配置できるかを示すために、0 ~ 255 の色を付けることができます。異なる色の物理ページ内の場所は、キャッシュ内で競合することはありません。
キャッシュを最大限に活用しようとするプログラマーは、 一度にキャッシュする必要のあるデータが 1 MiB だけになるようにプログラムのアクセスパターンを調整することで、キャパシティミスを回避することができます。しかし、アクセスパターンに競合ミスが発生しないようにすることも重要です。この問題を考える一つの方法は、プログラムが使用する仮想ページを分割し、物理ページに物理色が割り当てられていたのと同じように仮想色を割り当てることです。こうすることで、プログラマーはコードのアクセスパターンを調整し、同じ仮想色を持つページが同時に使用されないようにすることができます。このような最適化(ループネスト最適化など)に関する文献は豊富にあり、その多くは高性能コンピューティング(HPC)コミュニティから発信されています。
問題は、ある時点で使用されているすべてのページがそれぞれ異なる仮想色を持つ可能性がある一方で、一部のページは同じ物理色を持つ可能性があるということです。実際、オペレーティングシステムが物理ページを仮想ページにランダムかつ均一に割り当てると、一部のページが同じ物理色を持つ可能性が非常に高くなり、その結果、それらのページの場所がキャッシュ内で衝突することになります(これが誕生日パラドックスです)。
解決策は、オペレーティングシステムが異なる物理カラーページを異なる仮想カラーに割り当てようとすることです。これはページカラーリングと呼ばれる手法です。仮想カラーから物理カラーへの実際のマッピングはシステムのパフォーマンスには関係ありませんが、奇妙なマッピングは追跡が難しく、メリットもほとんどないため、ほとんどのページカラーリングのアプローチでは、物理ページカラーと仮想ページカラーを同じに保つようにしています。
オペレーティングシステムが各物理ページが1つの仮想色にのみマッピングされることを保証できる場合、仮想エイリアスは存在せず、プロセッサは誤処理時に追加の仮想エイリアスプローブを必要とせずに仮想インデックス付きキャッシュを使用できます。あるいは、OSは仮想色が変わるたびにキャッシュからページをフラッシュすることもできます。前述のように、このアプローチは初期のSPARCおよびRS/6000設計の一部で使用されていました。
ソフトウェアページカラーリング技術は、マルチコアプロセッサの共有ラストレベルキャッシュ(LLC)を効果的に分割するために使用されてきました。[ 35 ]このマルチコアプロセッサにおけるオペレーティングシステムベースのLLC管理は、Intelによって採用されています。[ 36 ]

現代のプロセッサには、相互に作用する複数のオンチップキャッシュが搭載されています。特定のキャッシュの動作は、キャッシュサイズ、キャッシュブロックサイズ、セット内のブロック数、キャッシュセット置換ポリシー、およびキャッシュ書き込みポリシー(ライトスルーまたはライトバック)によって完全に指定できます。[ 25 ]
特定のキャッシュ内のすべてのキャッシュブロックは同じサイズで同じアソシアティビティを持ちますが、通常、「上位レベル」のキャッシュ(レベル 1 キャッシュと呼ばれる)は、ブロック数、ブロックサイズ、セット内のブロック数が少なく、アクセス時間が非常に短くなります。「下位レベル」のキャッシュ(レベル 2 以下)は、ブロック数、ブロックサイズ、セット内のブロック数が徐々に増加し、アクセス時間は比較的長くなりますが、それでもメイン メモリよりはるかに高速です。[ 8 ]
キャッシュエントリの置換ポリシーは、プロセッサ設計者が実装するキャッシュアルゴリズムによって決定されます。場合によっては、異なる種類のワークロードに対応するために複数のアルゴリズムが提供されることもあります。
パイプライン型CPUは、命令フェッチ、仮想アドレスから物理アドレスへの変換、データフェッチといったパイプライン内の複数のポイントからメモリにアクセスします(従来のRISCパイプラインを参照)。これらのポイントごとに異なる物理キャッシュを使用するのが自然な設計であり、1つの物理リソースをパイプライン内の2つのポイントに割り当てて処理する必要がなくなります。そのため、パイプラインは必然的に少なくとも3つの独立したキャッシュ(命令キャッシュ、TLBキャッシュ、データキャッシュ)を持つようになり、それぞれが特定の役割に特化しています。
ビクティムキャッシュは、CPU キャッシュの交換時に追い出されたブロックを保持するために使用されるキャッシュです。ビクティム キャッシュはメイン キャッシュとその補充パスの間に位置し、メイン キャッシュから追い出されたデータ ブロックのみを保持します。ビクティム キャッシュは通常、完全な連想性を持ち、競合ミスの数を減らすことを目的としています。一般的に使用される多くのプログラムでは、すべてのアクセスに対して連想マッピングは必要ありません。実際、プログラムのメモリ アクセスのごく一部だけが、高い連想性を必要とします。ビクティム キャッシュはこの特性を利用して、これらのアクセスのみに高い連想性を提供します。これは、1990 年に DEC のNorman Jouppiによって導入されました。 [ 37 ]
IntelのHaswellプロセッサのCrystalwell [ 38 ]バリアントでは、プロセッサのレベル3キャッシュのビクティムキャッシュとして機能する、パッケージ上の128 MiB eDRAMレベル4キャッシュが導入されました。 [ 39 ] Skylakeマイクロアーキテクチャでは、レベル4キャッシュはビクティムキャッシュとして機能しなくなりました。[ 40 ]
キャッシュ特化の極端な例の 1 つは、Intel Pentium 4マイクロプロセッサに搭載されているトレース キャッシュ(実行トレース キャッシュとも呼ばれる) です。トレース キャッシュは、既にフェッチおよびデコードされた命令のトレースを保存することで、命令フェッチ帯域幅を増やし、消費電力 (Pentium 4 の場合)を削減するメカニズムです。[ 41 ]
トレースキャッシュは、命令がデコードされた後、または実行が終了した後に命令を格納します。一般的に、命令は個々の基本ブロックまたは動的命令トレースを表すグループとしてトレースキャッシュに追加されます。Pentium 4のトレースキャッシュは、x86命令のデコードによって生成されたマイクロオペレーションを格納し、マイクロオペレーションキャッシュの機能も提供します。これにより、次に命令が必要になったときに、マイクロオペレーションに再度デコードする必要がなくなります。[ 42 ] : 63-68
ライトコアレッシングキャッシュ[ 43 ]は、 AMDのBulldozerマイクロアーキテクチャのL2キャッシュの一部である特殊なキャッシュです。モジュール内の両方のL1DキャッシュからのストアはWCCを経由し、そこでバッファリングおよびコアレッシングされます。WCCの役割は、L2キャッシュへの書き込み回数を減らすことです。
マイクロオペレーションキャッシュ(μopキャッシュ、uopキャッシュ、またはUC)[ 44 ]は、命令デコーダまたは命令キャッシュから直接受け取った、デコードされた命令のマイクロオペレーションを格納する特殊なキャッシュです。命令をデコードする必要がある場合、μopキャッシュがチェックされ、デコードされた形式がキャッシュされている場合は再利用されます。利用できない場合は、命令がデコードされてからキャッシュされます。
Intel P6 プロセッサ ファミリーの代替フロントエンドとして μop キャッシュを説明した初期の研究の 1 つは、2001 年の論文「Micro-Operation Cache: A Power Aware Frontend for Variable Instruction Length ISA」です。[ 45 ]その後、Intel は、 Sandy Bridgeプロセッサや、 Ivy BridgeやHaswellなどの後継のマイクロ アーキテクチャにμop キャッシュを組み込みました。[ 42 ] : 121–123 [ 46 ] AMD は、 Zen マイクロ アーキテクチャに μop キャッシュを実装しました。[ 47 ]
完全に事前デコードされた命令をフェッチすることで、可変長の複雑な命令をより単純な固定長のマイクロオペレーションに繰り返しデコードする必要がなくなり、フェッチされた命令の予測、フェッチ、回転、および整列のプロセスが簡素化されます。μop キャッシュはフェッチおよびデコード ハードウェアを効果的にオフロードし、消費電力を削減し、デコードされたマイクロオペレーションのフロントエンドへの供給を改善します。μop キャッシュはまた、デコードされたマイクロオペレーションをバックエンドにより一貫して提供し、CPU のフェッチおよびデコード ロジックのさまざまなボトルネックを解消することでパフォーマンスを向上させます。[ 45 ] [ 46 ]
μopキャッシュはトレースキャッシュと多くの類似点がありますが、μopキャッシュの方がはるかに単純なため、電力効率が優れています。そのため、バッテリー駆動デバイスでの実装に適しています。トレースキャッシュの主な欠点は、動的に生成された命令トレースをキャッシュして再利用するかどうかを決定するヒューリスティックに必要なハードウェアの複雑さであり、これが電力効率の悪さにつながっています。[ 48 ]
ARMマイクロプロセッサで使用されるブランチターゲットキャッシュまたはブランチターゲット命令キャッシュ[49]は、分岐先の最初の数個の命令を保持する特殊なキャッシュです。これは、メモリシステムが命令を十分速く提供できるため、通常の命令キャッシュを必要としない低電力プロセッサで使用されます。ただし、これは連続する命令にのみ適用されます。新しいアドレスで命令フェッチを再開するには、依然として数サイクルのレイテンシが必要であり、制御転送後にパイプラインバブルが数サイクル発生します。ブランチターゲットキャッシュは、これらの数サイクル分の命令を提供することで、ほとんどの分岐後の遅延を回避します。
これにより、従来のフルタイム命令キャッシュよりもはるかに小さなキャッシュで、フルスピードでの動作が可能になります。
スマートキャッシュは、インテルが開発した、複数の実行コア向けのレベル2またはレベル3のキャッシュ方式です。
スマートキャッシュは、マルチコアプロセッサのコア間で実際のキャッシュメモリを共有します。コアごとに専用のキャッシュを使用する場合と比較して、コアがキャッシュスペースを均等に必要としない場合、全体のキャッシュミス率が低下します。その結果、他のコアが非アクティブになっている間、1つのコアがレベル2またはレベル3のキャッシュ全体を使用できます。[ 50 ]さらに、共有キャッシュにより、異なる実行コア間でメモリを共有するのが速くなります。[ 51 ]
もう一つの問題は、キャッシュのレイテンシとヒット率の根本的なトレードオフです。キャッシュが大きいほどヒット率は高くなりますが、レイテンシは長くなります。このトレードオフに対処するため、多くのコンピュータはマルチレベルキャッシュを使用しており、小さくて高速なキャッシュの後ろに、大きくて低速なキャッシュを配置しています。マルチレベルキャッシュは一般的に、最も高速で最小のキャッシュであるレベル1(L1)を最初にチェックし、ヒットすればプロセッサは高速で処理を進めます。そのキャッシュがミスした場合は、より低速でより大きな次のレベルのキャッシュであるレベル2(L2)をチェックし、外部メモリにアクセスする前にこれを繰り返します。
メインメモリと最速のキャッシュ間のレイテンシの差が大きくなるにつれて、一部のプロセッサは最大 3 レベルのオンチップ キャッシュを利用し始めました。価格重視の設計では、これを使用してキャッシュ階層全体をオンチップに引き込みましたが、2010 年代までに、最高性能の設計の一部は、4 番目のキャッシュ レベルとして、多くの場合eDRAMで実装され、マルチ チップ モジュールにマウントされる大規模なオフチップ キャッシュを持つようになりました。まれなケースでは、メインフレーム CPU IBM z15 (2019)では、L1 までのすべてのレベルが eDRAM で実装され、SRAM が完全に置き換えられています (キャッシュには、レジスタには SRAM がまだ使用されています)。AppleのARM ベースのApple Siliconシリーズ ( A14およびM1から開始) は、高性能コアごとに192 KiB の L1i キャッシュを備えており、これは異常に大きな量です。ただし、高効率コアには 128 KiB しかありません。それ以降、 IntelのLunar LakeやQualcommのOryonといった他のプロセッサも同様のL1iキャッシュサイズを実装している。
L3およびL4キャッシュの利点は、アプリケーションのアクセスパターンによって異なります。L3およびL4キャッシュを組み込んだ製品の例としては、以下のようなものがあります。
最後に、メモリ階層のもう一方の端にあるCPUレジスタファイル自体が、システム内で最小かつ最速のキャッシュとみなすことができます。その特別な特徴は、ソフトウェア(通常はコンパイラ)によってスケジューリングされることです。コンパイラは、例えばループネストの最適化のために、メインメモリから取得した値を保持するためのレジスタを割り当てます。しかし、レジスタリネーミングにより、ほとんどのコンパイラレジスタ割り当ては、実行時にハードウェアによってレジスタバンクに動的に再割り当てされるため、CPUは誤ったデータ依存関係を解消し、パイプラインの危険性を軽減することができます。
レジスタファイルにも階層構造が存在する場合がある。例えば、Cray-1(1976年頃)には、一般的に使用可能なアドレス「A」レジスタが8個とスカラーデータ「S」レジスタが8個あった。また、アクセスに時間がかかるものの、メインメモリより高速なアドレス「B」レジスタとスカラーデータ「T」レジスタがそれぞれ64個ずつ存在した。「B」レジスタと「T」レジスタは、Cray-1にデータキャッシュがなかったために設けられていた。(ただし、Cray-1には命令キャッシュは搭載されていた。)
マルチコアチップを検討する際、キャッシュを共有型にするか、各コアにローカルにするかという問題が生じます。共有キャッシュを実装すると、必然的に配線が増え、複雑さが増します。しかし、コアごとではなくチップごとにキャッシュを設けることで、必要なスペースを大幅に削減でき、より大きなキャッシュを搭載することが可能になります。
通常、L1 キャッシュを共有すると、結果としてレイテンシが増加し、各コアがシングル コア チップよりもかなり遅くなるため、望ましくありません。しかし、最上位レベルのキャッシュ (通常は L3、メモリにアクセスする前に最後に呼び出されるキャッシュ) については、シングル コアがキャッシュ全体を使用できる、異なるプロセスやスレッドがキャッシュされたデータを共有できるようにすることでデータの冗長性を減らす、使用されるキャッシュ コヒーレンシ プロトコルの複雑さを減らすなど、いくつかの理由からグローバル キャッシュを持つことが望ましいです。[ 53 ]例えば、3 レベルの 8 コア チップには、各コア用の L1 キャッシュ、コアのペアごとに 1 つの中間 L2 キャッシュ、およびすべてのコアで共有される 1 つの L3 キャッシュが含まれる場合があります。
共有される最上位レベルのキャッシュ(通常はメモリにアクセスする前に呼び出されるL3)は、通常、最終レベルキャッシュ(LLC)と呼ばれます。[ 54 ] LLCが複数のコア間で共有される場合、並列性を高めるための追加の手法が使用されます。これには、LLCを特定のメモリ アドレス範囲をアドレス指定し、独立してアクセスできる複数の断片にスライスすることが含まれます。[ 8 ] [ 55 ]
独立したキャッシュ構造では、命令とデータは別々にキャッシュされるため、キャッシュラインは命令またはデータのいずれかをキャッシュするために使用され、両方をキャッシュするためには使用されません。データと命令の変換ルックアサイドバッファを分離することで、さまざまな利点が実証されています。[ 56 ]統合構造では、この制約はなく、キャッシュラインを使用して命令とデータの両方をキャッシュできます。
マルチレベルキャッシュは、新たな設計上の決定事項をもたらします。たとえば、一部のプロセッサでは、L1 キャッシュ内のすべてのデータは、L2 キャッシュのどこかにも存在する必要があります。これらのキャッシュは、厳密に包含的と呼ばれます。他のプロセッサ ( AMD Athlonなど)は排他的キャッシュを備えています。データは、L1 キャッシュと L2 キャッシュのうち最大で 1 つに存在することが保証されており、両方に存在することはありません。さらに他のプロセッサ (Intel Pentium II、III、および4など) では、L1 キャッシュ内のデータが L2 キャッシュにも存在する必要はありませんが、多くの場合、存在する可能性があります。この中間的なポリシーには、普遍的に受け入れられている名前はありません。 [ 57 ] [ 58 ] 2 つの一般的な名前は、「非排他的」と「部分的に包含的」です。
排他キャッシュの利点は、より多くのデータを格納できることです。この利点は、排他L1キャッシュがL2キャッシュと同程度の場合に大きくなり、L2キャッシュがL1キャッシュより何倍も大きい場合は小さくなります。アクセス時にL1がミスし、L2がヒットした場合、L2のヒットしたキャッシュラインがL1のラインと交換されます。この交換は、インクルーシブキャッシュが行うように、L2からL1にラインをコピーするよりもかなり多くの作業が必要です。[ 58 ]
厳密に包含的なキャッシュの利点の 1 つは、マルチプロセッサ システムで外部デバイスまたは他のプロセッサがプロセッサからキャッシュ ラインを削除したい場合、プロセッサに L2 キャッシュをチェックさせるだけで済むことです。包含を強制しないキャッシュ階層では、L1 キャッシュもチェックする必要があります。欠点としては、L1 キャッシュと L2 キャッシュのアソシアティビティに相関関係があることです。L2 キャッシュのウェイ数がすべての L1 キャッシュの合計数以上でない場合、L1 キャッシュの実効アソシアティビティが制限されます。包含的なキャッシュのもう 1 つの欠点は、L2 キャッシュでエビクションが発生するたびに、包含性を維持するために、L1 の (おそらく) 対応するラインもエビクションする必要があることです。これはかなりの作業であり、L1 ミス率の上昇につながります。[ 58 ]
包括キャッシュのもう 1 つの利点は、より大きなキャッシュがより大きなキャッシュ ラインを使用できるため、セカンダリ キャッシュ タグのサイズが小さくなることです。(排他キャッシュでは、両方のキャッシュが同じサイズのキャッシュ ラインを持つ必要があり、L1 ミス、L2 ヒット時にキャッシュ ラインを交換できます。)セカンダリ キャッシュがプライマリ キャッシュより 1 桁大きく、キャッシュ データがキャッシュ タグより 1 桁大きい場合、節約されるタグ領域は、L1 キャッシュ データを L2 に格納するために必要な増分領域に匹敵する可能性があります。[ 59 ]
スクラッチパッドメモリ(SPM)は、コンピュータ用語ではスクラッチパッド、スクラッチパッドRAM、ローカルストアとも呼ばれ、計算、データ、その他の進行中の作業を一時的に保存するために使用される高速の内部メモリです。
特殊化とマルチレベルキャッシングの両方を説明するために、AMD Athlon 64 CPUの K8 コアのキャッシュ階層を以下に示します。[ 60 ]

K8には、命令キャッシュ、命令TLB、データTLB、データキャッシュの4つの専用キャッシュがあります。これらのキャッシュはそれぞれ、以下の用途に特化しています。
K8にはマルチレベルキャッシュも搭載されています。第2レベルの命令TLBとデータTLBがあり、それぞれ4 KiBのPTEマッピングのみを格納します。命令キャッシュ、データキャッシュ、および各種TLBは、大規模な統合L2キャッシュからデータを取得できます。このL2キャッシュはL1命令キャッシュとL1データキャッシュ専用であるため、8バイトの行はL1命令キャッシュ、L1データキャッシュ、またはL2キャッシュのいずれかにしか存在できません。ただし、データキャッシュ内の行がTLBのいずれかに存在するPTEを持つ可能性はあります。オペレーティングシステムは、メモリ内のページテーブルが更新される際にTLBの一部をフラッシュすることで、TLBの一貫性を維持する役割を担っています。
K8は、メモリには格納されない情報、すなわち予測情報もキャッシュします。これらのキャッシュは上記の図には示されていません。この種のCPUでは一般的ですが、K8はかなり複雑な 分岐予測機能を備えており、分岐が行われるかどうかを予測するテーブルや、分岐やジャンプのターゲットを予測するテーブルなどがあります。これらの情報の一部は、レベル1命令キャッシュと統合二次キャッシュの両方において、命令に関連付けられています。
K8は、二次キャッシュ内の命令に予測情報を格納するための興味深い手法を採用しています。二次キャッシュ内のラインは、データ一次キャッシュまたは命令一次キャッシュのどちらから追い出されたかに応じて、 ECCまたはパリティによって偶発的なデータ破損(例えば、アルファ粒子の衝突など)から保護されます。パリティコードはECCコードよりもビット数が少ないため、命令キャッシュからのラインには若干の余剰ビットが残ります。これらのビットは、当該命令に関連付けられた分岐予測情報をキャッシュするために使用されます。結果として、分岐予測器の実効履歴テーブルが大きくなり、精度が向上します。
他のプロセッサには、別の種類の予測器が搭載されています(例えば、 DEC Alpha 21264のストア・トゥ・ロード・バイパス予測器など)。
これらの予測器は、計算コストの高い情報を格納するという点でキャッシュの一種です。予測器について議論する際に用いられる用語の中には、キャッシュの用語と同じものもあります(例えば、分岐予測器におけるヒットなど)が、予測器は一般的にキャッシュ階層の一部とは考えられていません。
K8プロセッサは、命令キャッシュとデータキャッシュの一貫性をハードウェアで維持します。つまり、ストア命令の直後の命令にストア命令を実行すると、その直後の命令も変更されます。AlphaやMIPSファミリーなどの他のプロセッサは、命令キャッシュの一貫性を維持するためにソフトウェアに依存しています。ストア命令は、プログラムがオペレーティングシステムの機能を呼び出して一貫性を確保するまで、命令ストリームに反映されるとは限りません。

コンピュータ工学では、タグRAMを使用して、CPUキャッシュに現在格納されているメモリ位置を指定します。[ 61 ] [ 62 ]単純な直接マップ設計では、高速SRAMを使用できます。高次の連想キャッシュは通常、コンテンツアドレス指定可能なメモリを使用します。
キャッシュ読み出しは、1サイクル以上を要する最も一般的なCPU操作です。プログラムの実行時間は、レベル1データキャッシュヒットのレイテンシに非常に敏感です。そのため、キャッシュを可能な限り高速化するために、多大な設計労力、そして多くの場合、電力とシリコン面積が費やされます。
最もシンプルなキャッシュは、仮想インデックス付きダイレクトマップキャッシュです。仮想アドレスは加算器で計算され、アドレスの関連部分が抽出されてSRAMのインデックスとして使用され、ロードされたデータが返されます。データはバイトシフターでバイト境界に揃えられ、そこから次の処理に渡されます。内側のループではタグチェックは不要で、実際にはタグを読み取る必要すらありません。パイプラインの後半、ロード命令が完了する前に、ロードされたデータのタグを読み取り、仮想アドレスと照合してキャッシュヒットがあったことを確認する必要があります。キャッシュミスがあった場合は、要求されたキャッシュラインでキャッシュが更新され、パイプラインが再開されます。
連想キャッシュは、キャッシュのエントリを選択するために何らかのタグを読み取る必要があるため、より複雑です。Nウェイセットアソシアティブレベル1キャッシュは通常、N個のタグとN個のデータを並列に読み取り、一致するタグに関連付けられたデータを選択します。レベル2キャッシュは、最初にタグを読み取ることで消費電力を節約し、データSRAMから読み取るデータ要素を1つだけにする場合もあります。

隣の図は、アドレスの各フィールドの使用方法を明確にするためのものです。アドレスビット31が最上位ビット、ビット0が最下位ビットです。この図は、 64バイト(B)ライン、32ビットの読み出し幅、32ビットの仮想アドレスを持つ、 4 KiB、2ウェイセットアソシアティブ、仮想インデックス付き、仮想タグ付きキャッシュのSRAM、インデックス、および多重化を示しています。
キャッシュは4 KiBで64 Bラインあるため、キャッシュには64ラインしかありません。そのため、32行のタグSRAMから一度に2行ずつ読み取ります。タグSRAMにはそれぞれ21ビットのタグが2つずつあります。タグSRAMとデータSRAMのインデックス付けには、仮想アドレスビット31~6の任意の関数を使用できますが、最下位ビットを使用するのが最も簡単です。
同様に、キャッシュは4 KiBで、4 Bの読み出しパスを持ち、アクセスごとに2つの方法で読み出しを行うため、データSRAMは512行×8バイト幅になります。
より現代的なキャッシュとしては、16 KiB、4ウェイセットアソシアティブ、仮想インデックス、仮想ヒント、物理タグ付き、32 Bライン、32ビット読み取り幅、36ビット物理アドレスなどが考えられます。このようなキャッシュの読み取りパスの繰り返しは、上記のパスと非常によく似ています。タグの代わりに仮想ヒントが読み取られ、仮想アドレスのサブセットと照合されます。パイプラインの後半で、仮想アドレスはTLBによって物理アドレスに変換され、物理タグが読み取られます(仮想ヒントによってキャッシュのどちらの方向を読み取るかが示されるため、読み取られるのは1つだけです)。最後に、物理アドレスが物理タグと比較され、ヒットが発生したかどうかが判断されます。
SPARCの設計の中には、仮想アドレス加算器をSRAMデコーダに統合することで、L1キャッシュの速度を数ゲート遅延分向上させたものもある。
キャッシュ技術の初期の歴史は、仮想メモリの発明と利用に密接に関係しています。半導体メモリの希少性とコストのため、1960年代の初期のメインフレームコンピュータは、プログラムが使用するフラットな仮想メモリ空間にマッピングされた複雑な物理メモリ階層を使用していました。メモリ技術は、半導体、磁気コア、ドラム、ディスクに及びました。プログラムが認識し使用する仮想メモリはフラットであり、キャッシュを使用して、プロセッサがアクセスする前にデータと命令を最速のメモリにフェッチしました。キャッシュサイズを最適化するために広範な研究が行われました。最適な値は使用するプログラミング言語に大きく依存することがわかりました。Algolは最小のキャッシュサイズを必要とし、FortranとCobolは最大のキャッシュサイズを必要としました。
マイクロコンピュータ技術の初期の頃は、メモリへのアクセス速度はレジスタへのアクセス速度よりわずかに遅いだけでした。しかし、1980年代以降[ 63 ]、プロセッサとメモリの性能差は拡大してきました。マイクロプロセッサはメモリよりもはるかに速く進化しており、特に動作周波数の面で顕著です。そのため、メモリが性能のボトルネックとなりました。技術的にはメインメモリ全体をCPUと同じ速度にすることは可能でしたが、より経済的に実現可能な道が取られました。それは、低速メモリを大量に使用しつつ、性能差を緩和するために小型の高速キャッシュメモリを導入するというものです。これにより、同じ価格で桁違いに容量が増え、総合的な性能はわずかに低下するだけで済みました。
TLBの最初の使用例はGE 645 [ 64 ]とIBM 360/67 [ 65 ]で、どちらもTLBとして連想メモリを使用していた。
1982年に発売された68010は、「ループモード」と呼ばれる、わずか2つの命令で構成されるループ処理を高速化する、小型で特殊な命令キャッシュを備えていました。 1984年に発売された68020は、これを256バイトの標準的な命令キャッシュに置き換え、真のオンチップキャッシュメモリを搭載した最初の68kシリーズプロセッサとなりました。
1987年にリリースされた68030は、基本的に68020コアに256バイトのデータキャッシュ、オンチップメモリ管理ユニット(MMU)、プロセス縮小、およびキャッシュのバーストモードを追加したものです。
1990年に発売された68040は、それぞれ4キロバイトの命令キャッシュとデータキャッシュを分離して搭載している。
1994年にリリースされた68060は、8 KiBのデータキャッシュ(4ウェイアソシアティブ)、8 KiBの命令キャッシュ(4ウェイアソシアティブ)、96バイトのFIFO命令バッファ、256エントリの分岐キャッシュ、および64エントリのアドレス変換キャッシュMMUバッファ(4ウェイアソシアティブ)を備えています。

x86マイクロプロセッサが386 で20MHz以上のクロック周波数に達すると、パフォーマンス向上のために、システムに少量の高速キャッシュメモリが搭載されるようになった。これは、メインメモリに使用されていたDRAMのレイテンシが最大120ナノ秒と大きく、リフレッシュサイクルも必要だったためである。キャッシュは、当時10~25ナノ秒程度のレイテンシを持つ、より高価ではあるが大幅に高速なSRAMメモリセルで構成されていた。初期のキャッシュはプロセッサの外部にあり、通常はマザーボード上に8個または9個のDIPデバイスとして配置され、ソケットに挿入することで、オプションの追加機能またはアップグレード機能としてキャッシュを有効にしていた。
Intel 386プロセッサの一部のバージョンは、16~256 KiBの外部キャッシュをサポートすることができた。
486プロセッサでは、8 KiB のキャッシュが CPU ダイに直接統合されました。このキャッシュは、より低速なオン マザーボード、つまりレベル 2 (L2) キャッシュと区別するために、レベル 1 または L1 キャッシュと呼ばれました。これらのオン マザーボード キャッシュははるかに大きく、最も一般的なサイズは 256 KiB でした。一部のシステムボードには、64 バイトまたは 128 K バイトのキャッシュ メモリを持つIntel 485 Turbocacheドーター カード用のソケットが含まれていました。 [ 68 ] [ 69 ]オン マザーボード キャッシュの人気はPentium MMX の時代まで続きましたが、 SDRAMの導入とバス クロック レートと CPU クロック レートの差が拡大したことにより、オン マザーボード キャッシュはメイン メモリよりわずかに高速になっただけになり、時代遅れになりました。
x86マイクロプロセッサにおけるキャッシュ実装の次の発展は、Pentium Proから始まった。Pentium Proは、セカンダリキャッシュをマイクロプロセッサと同じパッケージに搭載し、マイクロプロセッサと同じ周波数で動作させた。
マザーボード上のキャッシュは、以前インテルがマザーボード上のキャッシュで使用していたSocket 7を採用していたAMD K6-2およびAMD K6-IIIプロセッサのおかげで、長期間にわたって人気を博しました。K6-IIIは256 KiBのオンダイL2キャッシュを搭載し、オンボードキャッシュをL3と呼ばれる第3レベルキャッシュとして活用しました(最大2 MiBのオンボードキャッシュを搭載したマザーボードが製造されました)。Socket 7が廃止されると、オンマザーボードキャッシュはx86システムから姿を消しました。
3 レベル キャッシュは、Intel Xeon MP「Foster Core」[ 70 ]の導入時に初めて再び使用されるようになり、L3 キャッシュが CPU ダイに追加されました。新しいプロセッサ世代では合計キャッシュ サイズがますます大きくなるのが一般的になり、最近 (2011 年現在) では、レベル 3 キャッシュ サイズが数十メガバイトになることも珍しくありません。[ 71 ]
Intel はHaswellマイクロアーキテクチャでレベル 4 オン パッケージ キャッシュを導入しました。Intelの統合 Iris Pro グラフィックスのGT3eバリアントを搭載したCrystalwell [ 38 ] Haswell CPU は、同じパッケージ上に 128 MiB の組み込み DRAM ( eDRAM ) を実質的に備えています。この L4 キャッシュは、オン ダイ GPU と CPU 間で動的に共有され、CPU の L3 キャッシュのビクティム キャッシュとして機能します。 [ 39 ]
Apple M1 CPUは、コアの種類に応じて、コアごとに128 KiBまたは192 KiBのL1命令キャッシュを備えています(これはレイテンシやシングルスレッド性能に重要です)。これは、あらゆるCPUタイプ(ノートパソコンに限らず)の中でも異例に大きなL1キャッシュです。ノートパソコンの場合、キャッシュメモリの合計サイズはそれほど大きくなく(スループットには合計サイズの方が重要です)、IBMのメインフレームでは、はるかに大きな合計サイズ(例えばL3やL4)が利用可能です。
初期のキャッシュ設計は、キャッシュとRAMの直接コストと平均実行速度にのみ焦点を当てていました。より最近のキャッシュ設計では、エネルギー効率、耐障害性、その他の目標も考慮されています。[ 72 ] [ 73 ]
コンピュータアーキテクトがキャッシュサイクル時間、エネルギー、面積のトレードオフを検討するのに役立つツールがいくつかあります。CACTIキャッシュシミュレータ[ 74 ]とSimpleScalar命令セットシミュレータは、オープンソースのオプションの2つです。
マルチポートキャッシュとは、一度に複数のリクエストを処理できるキャッシュのことです。従来のキャッシュにアクセスする場合、通常は単一のメモリアドレスを使用しますが、マルチポートキャッシュでは、プロセッサとキャッシュを介して接続されたポートの数であるN個のアドレスを同時に要求できます。この利点は、パイプラインプロセッサがパイプラインの異なるフェーズからメモリにアクセスできることです。また、異なるキャッシュレベルを介してスーパースカラプロセッサの概念を実現できるという利点もあります。
ケンブリッジでは 2 つのトンネル ダイオード ストアが開発されました。1 つはオペランドのフェッチを高速化するために非常にうまく機能し、もう 1 つは命令のフェッチを高速化することを目的としていました。ほとんどの命令は順番に実行されるため、命令がフェッチされると、そのワードはフェッチ アドレスを 32 で割った余りで指定された場所にスレーブ ストアに格納され、フェッチ アドレスの残りのビットも格納されました。目的のワードがスレーブにある場合は、メイン メモリではなくスレーブから読み出されました。これにより、最大 32 命令の長さの命令ループが大幅に高速化され、最大 64 ワードのループでは効果が小さくなります。
キャッシュはコアあたり32KB、L2キャッシュは4コアクラスタあたり4.5MB、共有LLCキャッシュは最大15MB。
スキューアソシアティブキャッシュは、従来のセットアソシアティブキャッシュに比べて2つの大きな利点があることが示されています。
{{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク){{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク){{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク){{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク)