JA EN
体系計算機アーキテクチャ
·★ 会員·論文·12分で読めます

CPUパイプラインと分岐予測 — 1クロックの中の工場

1命令が1クロックで終わるように見えるのは、工場のラインと同じ流れ作業だから。5段パイプラインの組み立てから、流れを止める3種のハザード、分岐予測と投機実行、そして投機の副産物が情報漏洩になったSpectreまでを、前提知識ゼロから追いかけます。

対象textタスクhardware

Spectre Attacks: Exploiting Speculative Execution

一次資料 — この記事の根拠

この解説の公開 2026-08-25

Spectre Attacks: Exploiting Speculative ExecutionarXiv:1801.01203論文ページ·PDF
Meltdown: Reading Kernel Memory from User SpacearXiv:1801.01207論文ページ·PDF

1クロックで1命令、という言い方の正体

仕様には「3.5 GHz」と書いてあります。1秒に35億回の拍。命令はおおむね1拍で1個ずつ片づく——そう聞くと、1命令が0.3ナノ秒で終わっているように思えます。実際は違います。1命令が入口から出口まで通るには何拍もかかる。それでも1拍に1個ずつ出てくるのは、何個もの命令が別々の工程で同時に作業されているからです。

コインランドリーで考えると早い。洗濯30分、乾燥30分、たたむ30分。1人分は必ず90分で、4人を律儀に1人ずつ通せば360分です。けれど乾燥機へ移した瞬間に次の人が洗濯機を回し始めれば、最初の1人が終わるのは90分後のままなのに、その後は30分おきに1人ずつ終わる。全体では180分になります。

混ぜてはいけない量が2つあります。1人分が終わるまでがレイテンシ(90分)、単位時間に何人分片づくかがスループット(30分に1人)。パイプラインはレイテンシを縮めず、工程の境目の手間でむしろわずかに伸ばします。それでもスループットが3倍になるから採用されている、というのが骨格です。

5段の工場ライン

古典的な構成が5段パイプラインです。1命令の仕事を5工程に割ります。

略号 やること
1 IF 命令をメモリから取ってくる(Instruction Fetch)
2 ID 命令を解読し、必要なレジスタの値を読む(Decode)
3 EX 計算する。足す、比べる、アドレスを作る(Execute)
4 MEM データメモリを読み書きする(ロード/ストアのときだけ実質的な仕事)
5 WB 結果をレジスタに書き戻す(Write Back)

工程の境目にはパイプラインレジスタという小さな棚があり、途中の結果をそこに置いてから次へ渡します。おかげで各工程は、前後の事情を知らずに自分の棚だけ見て働けます。NN 個の命令を kk 段に流したときの所要時間は、1段ぶんの時間を tt として次のとおりです。

T=(N+k1)tT = (N + k - 1)\, t
(1)

要するに「最初の1個が出るまで kk 段ぶん待ち、あとは1段ぶんの時間ごとに1個ずつ出てくる」。NN が大きければ k1k-1 は誤差で、1命令あたりは tt に近づきます。パイプライン無しなら ktkt ですから、理屈のうえでは kk 倍です。ただし段を細かく割るほど1段は短くなる一方、棚に置く時間は割っても縮まないので、増やせる段数には限りがあります。

流れが止まる3つの理由

きれいに流れれば1拍1命令。それを妨げるものをハザードと呼び、原因は3種類しかありません。

構造ハザード — 同じ設備を2人が同時に使いたい。乾燥機が1台なら片方が待たされます。命令もデータも同じメモリから取ると起きるので、現代のCPUは命令キャッシュとデータキャッシュを分けて避けています。

データハザード — 前の命令の結果が次の命令の材料になっている。前の人がたたみ終えた服を次の人が着ようとしている状態で、並べただけではまだ乾いていません。

制御ハザード — 次にどの命令を取ればいいかが決まっていない。分岐の判定が出るまで入口が止まる問題で、この記事の後半はほとんどこれとの戦いです。

データハザードは配線で回避する

a = b + c の次に d = a - e があるとします。1本目が計算を終えるのはEX段、書き戻されるのはWB段なので、素直に作ると2本目はID段で古い値を掴みます。解決は直接的で、EXの出力をEXの入力へ配線で戻すだけ。これをフォワーディング(バイパス)と呼びます。乾燥機から直接手渡しするようなものです。

配線で消せない組み合わせが1つだけあります。ロード-ユース ハザードです。メモリから読んだ値が出るのはMEM段の終わりで、次の命令のEX段には間に合わない。時間をさかのぼる配線は引けないので1拍待つ(ストール)しかありません。だからコンパイラはロードの直後に「その値を使わない命令」を置こうとします。最適化ビルドで命令の順序が原型をとどめないのは、この埋め合わせの結果です。

制御ハザード — 分岐は「行き先が分からない」

分岐の厄介さは、判定と取得の時間差です。IF段は毎拍「次の命令」を取りにいくのに、if (x > 0) の判定が出るのはEX段のあたり。その数拍、入口は何を流すか分かりません。しかもコードのおおむね5〜10命令に1本は分岐です。ループの底、関数呼び出し、switch、仮想関数——プログラムは実質、分岐でできています。

そこでCPUは待たずに賭けることにしました。予測してその先を流し始め、当たれば損失ゼロ、外れたら流した分を捨てて取り直す。損得を式にすると、1命令あたりの平均サイクル数(CPI)はこうなります。

は全命令に占める分岐の割合、 は外す確率、 は1回外して捨てる拍数(ミスペナルティ)。つまり「理想の1拍に、分岐の多さ × 外す確率 × 1回の損を足す」。分岐が5命令に1本()でペナルティ15拍なら、精度95%で 、99%なら 。外す確率の4ポイント差で実行時間が1割以上動きます。

この先にあるもの

§

ここから先は会員限定です

解説記事371本・教科書26章・学生モード48単元・論文精読6本が、月額¥490ですべて読み放題になります。新しい解説は毎日3本ずつ増えます。いつでも解約でき、解約後も期間の終わりまで読めます。

会員の方はログインすると続きが表示されます

参考文献

  1. Spectre Attacks: Exploiting Speculative Execution. arXiv:1801.01203論文ページ·PDF
  2. Meltdown: Reading Kernel Memory from User Space. arXiv:1801.01207論文ページ·PDF

本記事は上記論文の本文にもとづいて執筆しています。数値・主張は原典を優先してください。

コメント

コメントにはログインが必要です