投機的アウトオブオーダー (OoO) 実行の基本ルールは次のとおりです。
- 命令がプログラム順に実行されているという錯覚を維持する
- 誤った推測が検出された場合にロールバックできるもの、および他のコアが間違った値を保持していることを観察できないものに推測が含まれていることを確認してください。物理レジスタ、命令順序を追跡するバックエンド自体はありますが、キャッシュはありません。キャッシュは他のコアと一貫性があるため、ストアは非投機的になるまでキャッシュにコミットしてはなりません。
OoO exec は通常、引退するまですべてを投機的なものとして扱うことによって実装されます。すべてのロードまたはストアでエラーが発生する可能性があり、すべての FP 命令で FP 例外が発生する可能性があります。分岐は (例外と比較して) 特別です。ただし、分岐の予測ミスがまれではないため、分岐ミスの早期検出とロールバックを処理する特別なメカニズムが役立ちます。
はい、キャッシュ可能なロードは副作用がないため、投機的に実行できます。
ストア バッファーのおかげで、ストア命令を投機的に実行することもできます。 ストアの実際の実行では、アドレスとデータをストア バッファに書き込むだけです。 (関連: Intel ハードウェアのストア バッファーのサイズ? ストア バッファーとは正確には何ですか? は、 x86 に重点を置いて、これよりも技術的になります。この回答は、ほとんどの ISA に適用できると思います。)
L1d キャッシュへのコミットは、ストア命令が ROB からリタイアした後で発生します。つまり、ストアが非投機的であることがわかっている場合、関連するストア バッファ エントリは「卒業」し、キャッシュにコミットする資格があり、グローバルに可視になります。ストア バッファーは、他のコアが認識できるものから実行を分離し、このコアをキャッシュ ミス ストアから隔離するため、インオーダー CPU でも非常に便利な機能です。
ストア バッファ エントリが「卒業」する前に、誤った推測でロールバックするときに、それを指す ROB エントリと一緒に破棄することができます。
(これが、厳密に順序付けされたハードウェア メモリ モデルでも StoreLoadの再順序付けを許可する理由です実際にコミットするストア)。
ストア バッファは実質的に循環バッファです。エントリはフロントエンドによって割り当てられ (alloc/rename パイプライン ステージ中に)、L1d キャッシュへのストアのコミット時に解放されます。(これはMESIを介して他のコアと一貫性を保っています)。
x86 のような厳密に順序付けされたメモリ モデルは、ストア バッファーから L1d に順番にコミットすることで実装できます。エントリはプログラム順に割り当てられるため、ストア バッファは基本的にハードウェアの循環バッファになります。弱い順序の ISA は、ストア バッファーのヘッドがまだ準備ができていないキャッシュ ライン用である場合、新しいエントリを参照できます。
一部の ISA (特に弱い順序付け) は、ストア バッファー エントリのマージも行い、32 ビット ストアのペアから L1d への単一の 8 バイト コミットを作成します。
キャッシュ可能なメモリ領域の読み取りには副作用がないと想定されており、OoO exec、ハードウェア プリフェッチなどによって投機的に実行できます。誤った投機はキャッシュを「汚染」し、実行の真のパスでは実行されないキャッシュラインに触れることで帯域幅を浪費する可能性があります (そして、TLB ミスの投機的なページウォークをトリガーすることさえあります) が、それが唯一の欠点です1。
MMIO 領域 (ネットワーク カードや SATA コントローラーに何かを実行させるなど、読み取りに副作用がある場所)は、その物理アドレスからの投機的読み取りが許可されていないことを CPU が認識できるように、キャッシュ不可としてマークする必要があります。 これを間違えると、システムが不安定になります-私の答えは、投機的ロードについてあなたが求めているのと同じ詳細の多くをカバーしています。
高性能 CPU には、L1d キャッシュでミスしたものを含め、実行中のロードを追跡するための複数のエントリを持つロード バッファがあります。(インオーダー CPU でもヒット アンダー ミスとミス アンダー ミスを許可し、命令がまだ準備ができていないロード結果レジスタを読み込もうとした場合にのみストールします)。
OoO exec CPU では、1 つのロード アドレスが別のロード アドレスより先に準備できた場合にも、OoO exec を許可します。最終的にデータが到着すると、ロード結果からの入力を待っている命令は実行可能になります (他の入力も準備ができている場合)。そのため、ロード バッファ エントリをスケジューラ (一部の CPU ではリザベーション ステーションと呼ばれる) に接続する必要があります。
L2 ヒットのデータが L2 から到着する可能性があるサイクルで積極的に起動しようとすることで、Intel CPU が待機中の uop を具体的にどのように処理するかについては、RIDL の脆弱性とロードの「リプレイ」についても参照してください。
脚注 1 : この欠点と、マイクロ アーキテクチャの状態 (キャッシュ ラインのホットまたはコールド) をアーキテクチャの状態 (レジスタ値) に検出/読み取るためのタイミング サイドチャネルと組み合わせることで、Spectre が可能になります。( https://en.wikipedia.org/wiki/Spectre_(security_vulnerability)#Mechanism )
メルトダウンについても理解することは、間違ったパスにあることが判明した投機的ロードの障害抑制を Intel CPU がどのように処理するかの詳細を理解するのに非常に役立ちます。 http://blog.stuffedcow.net/2018/05/meltdown-microarchitecture/
そして、確かに、読み取り/書き込み操作がサポートされています
はい、命令uopにデコードする最新のx86について話している場合は、論理的に分離されたロード/ ALU /ストア操作にそれらをデコードすることによって。ロードは通常のロードと同じように機能し、ストアは ALU の結果をストア バッファに入れます。3 つの操作はすべて、別の命令を記述した場合と同様に、順不同のバックエンドによって正常にスケジュールできます。
原子RMWを意味する場合、それは本当に投機的ではありません. キャッシュはグローバルに表示され (共有要求はいつでも発生する可能性があります)、ロールバックする方法はありません ( Intel がトランザクション メモリに対して行っていることを除いて...)。キャッシュに間違った値を入れてはいけません。'int num' に対して num++ をアトミックにできますか?を参照してください。特に最新の x86 で、ロードとストアコミットの間のその行に対する共有/無効化要求への応答を遅らせることによって、アトミック RMW がどのように処理されるかについての詳細。
lock add [rdi], eax
ただし、それはパイプライン全体をシリアル化するという意味ではありません。並べ替えられる命令はロードとストアだけですか? 他の独立した命令の投機的な OoO exec がアトミック RMW の周りで発生する可能性があることを示しています。lfence
(対、ROB を排出するような exec バリアで何が起こるか)。
多くのRISC ISAは、単一のアトミックRMW命令ではなく、ロードリンク/ストア条件付き命令を介してアトミックRMWのみを提供します。
[読み取り/書き込み操作...]、少なくともある程度は、一部のCPUではレジスタ自体がCPUキャッシュに物理的に配置されているためです。
は?誤った前提であり、その論理は意味がありません。別のコアがいつでもキャッシュを共有するように要求する可能性があるため、キャッシュは常に正しい必要があります。このコア専用のレジスタとは異なります。
レジスタ ファイルはキャッシュのように SRAM から構築されますが、分離されています。ボード上にSRAMメモリ(キャッシュではない)を備えたマイクロコントローラがいくつかあり、レジスタはその空間の初期バイトを使用してメモリ マップされます。(例: AVR)。しかし、そのどれも、順不同の実行とはまったく関係がないようです。メモリをキャッシュしているキャッシュラインは、レジスタ値を保持するなど、まったく異なる目的で使用されているキャッシュラインと同じではありません。
また、投機的実行を行うためにトランジスタの予算を費やしている高性能 CPU が、キャッシュとレジスタ ファイルを組み合わせることも、実際にはあり得ません。次に、読み取り/書き込みポートをめぐって競合します。読み取りポートと書き込みポートの合計を持つ 1 つの大きなキャッシュは、小さな高速レジスタ ファイル (多くの読み取り/書き込みポート) と小さな (32kiB のような) L1d キャッシュよりもはるかに高価です (面積と電力)。2 つの読み取りポートと 1 つの書き込みポートがあります。ポート。同じ理由で、分割 L1 キャッシュを使用し、最新の CPU のコアごとに 1 つの大きなプライベート キャッシュではなく、マルチレベル キャッシュを使用しています。ほとんどのプロセッサで、L1 キャッシュのサイズが L2 キャッシュのサイズよりも小さいのはなぜですか?
関連する読書/背景: