0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

分岐ハザードとは何か?CPU設計を悩ませる制御ハザードの正体

Posted at

はじめに

CPUの高速化には「パイプライン処理」が欠かせませんが、それに伴って発生するのが ハザード(hazard) です。その中でも特にやっかいなのが、 分岐命令によって次に実行すべき命令が分からなくなる「分岐ハザード(branch hazard)」 です。

本記事では、分岐ハザードの概要、なぜ問題なのか、どのように対策されているのかをわかりやすく解説します。

パイプライン処理とは?

パイプライン処理とは、 1つの命令を複数の工程(ステージ)に分割し、それぞれのステージを別の命令が同時に処理する ことで、CPU全体として並列処理を実現する技術です。まるで工場のベルトコンベアのように、命令を次々と流れ作業で片付けていきます。

命令1: フェッチ → デコード → 実行 → ...
命令2:        フェッチ → デコード → ...
命令3:               フェッチ → ...

一般的な5ステージのパイプラインは以下のようになります。

  • IF (Instruction Fetch): メモリから命令を読み込む(フェッチ)
  • ID (Instruction Decode): 命令を解読する(デコード)
  • EX (Execute): 命令を実行する
  • MEM (Memory Access): メモリへの読み書きを行う
  • WB (Write Back): 結果をレジスタに書き込む

理想的な状況では、クロックが進むごとに各ステージが同時に動き、CPUは1クロックあたり1命令を完了させる高いスループットを達成します。

分岐ハザードとは?

パイプライン処理は、次に実行する命令が分かっている場合に最も効率的に機能します。しかし、分岐命令が登場すると、その前提が崩れます。

分岐命令が登場すると、その結果が分かるまで次に何を実行すべきか確定できません
それでもCPUは高速化のため、分岐の結果を待たずに命令を先読みします。

100: BEQ R1, R2, 200   ; R1とR2が等しければ、アドレス200へジャンプ
104: ADD R3, R4, R5    ; 分岐しない場合に実行する命令
...
200: SUB R6, R7, R8    ; 分岐した場合に実行する命令

BEQ命令(Branch if Equal) の結果、つまりR1とR2が等しいかどうかは、EXステージ(実行段階)まで進まないと分かりません。

しかし、パイプラインは止まりません。CPUはBEQ命令の結果を待たずに、次のADD命令をIFステージ、IDステージへと進めてしまいます。

もし、EXステージで「分岐する(ジャンプする)」と判明したらどうなるでしょうか?

  • 大失敗! パイプラインに取り込んでしまったADD命令は、本来実行すべきではなかった命令です。
  • 処理の破棄(フラッシュ): CPUは、この間違って読み込んだ命令をすべて捨てなければなりません。
  • 正しい命令の再取得(再フェッチ): そして、正しい分岐先(アドレス 200 の SUB 命令)から、改めて命令をフェッチし直します。

この「分岐の結果が分かるまで次に実行すべき命令が確定できず、パイプラインに無駄な待ち時間(ストール)や処理の破棄(フラッシュ)が発生する」こと、これが分岐ハザードの正体です。

  • ✅ フェッチした命令をすべて破棄(フラッシュ)
  • 再フェッチしてやり直し
  • パフォーマンス低下

分岐ハザードが「ひどい問題」と言える理由

分岐ハザードが「CPU設計における最大の頭痛の種」とまで言われる理由は、主に3つあります。

1. パイプラインが深いほどペナルティが甚大

現代の高性能CPUでは、処理をさらに細分化して10段、20段、あるいはそれ以上の深いパイプラインを持つのが一般的です。パイプラインが深ければ深いほど、分岐をミスしたときに破棄しなければならない命令の数が増え、失われるサイクル(ペナルティ)が致命的なレベルになります。

2. 分岐はプログラムの至る所に存在する

if-elseによる条件分岐、forやwhileによるループ、関数の呼び出しなど、我々が書くコードは分岐命令の塊です。研究によれば、全実行命令の15%〜20%が分岐命令であるとされており、その影響は決して無視できません。

3. 投機的実行の複雑化とセキュリティリスク

後述する対策「投機実行」は、間違った場合の「状態の巻き戻し(ロールバック)」機構を必要とし、CPUの設計を非常に複雑にします。また、この仕組みが、近年話題になったSpectreやMeltdownといったセキュリティ脆弱性の根源にもなりました。

分岐ハザードへの対策

1. 分岐遅延スロット (Branch Delay Slot)

古典的なRISCアーキテクチャ(MIPSなど)で採用された、ユニークな解決策です。

  • 「分岐命令の直後にある1命令だけは、分岐するかどうかにかかわらず必ず実行する」というルールを設ける
  • 分岐の結果を待つ間にパイプラインにできてしまう「穴」を、その「必ず実行される」命令で埋めることで、CPUの遊休時間を減らす

コンパイラは、分岐命令の前にあり、分岐の結果に影響されない安全な命令を見つけて、この「遅延スロット」に移動させる最適化を行います。しかし、パイプラインが深化した現代では、1スロットではペナルティを埋めきれないことや、コンパイラの負担が大きいことから、主流ではなくなっています。

2. 分岐予測 (Branch Prediction)

現在主流となっている対策です。「コケる前に杖を突く」戦略で、分岐命令がどちらに進むかを積極的に予測し、予測した側の命令をパイプラインに送り込みます。

  • 静的分岐予測: 「ループのような後方への分岐は常に成立(Taken)と予測する」「常に分岐しない(Not Taken)と予測する」など、プログラムの構造から決まる単純なルールで予測する
  • 動的分岐予測: CPU内に分岐履歴テーブル (BHT: Branch History Table) という小さなメモリを持ち、個々の分岐命令が過去にどちらに分岐したかを記録する

さらに、分岐先のメモリアドレスをキャッシュしておく分岐先バッファ (BTB: Branch Target Buffer) と組み合わせることで、「分岐するかどうか」と「どこへジャンプするか」を高速に予測します。

3. 投機実行 (Speculative Execution)

分岐予測をさらに一歩進めた、非常にアグレッシブな手法です。

  • 分岐予測の結果を信じて、確定する前に命令をどんどん実行してしまう
  • 予測が正しければ、計算結果をそのまま使い、大幅な時間短縮になる
  • 予測が外れたら、投機的に実行したすべての命令の結果を きれいに破棄(ロールバック) し、何もなかったかのように正しいパスの実行を再開する

この「見切り発車」はハイリスク・ハイリターンですが、分岐予測の精度が90%以上と非常に高いため、トータルでは絶大なパフォーマンス向上をもたらします。現代の高性能CPUの根幹をなす技術です。

おわりに

分岐ハザードはCPUの命令実行を効率的に行ううえで避けて通れない問題です。
「たった1つのif文」のために、CPUが10命令も無駄にする世界——それがコンピュータアーキテクチャの面白さでもあります。

CPUの奥深さに触れる第一歩として、ぜひ知っておきたい概念ですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?