VLIWとEPICアーキテクチャ ─ 静的スケジューリングの設計判断
なぜItaniumは消えたのか。コンパイラが並列性を明示するVLIW/EPICの命令束・述語実行・投機ロードと、可変メモリ遅延に敗れた理由を原理から押さえ、設計判断の本質を掴めます。
- 1.VLIW/EPICは命令の並列スケジューリングをハードウェアからコンパイラへ移し、固定幅の命令束に独立演算を詰めて同時発行する。配線と電力を食う動的スケジューラを省ける。
- 2.述語実行で分岐を消し、投機ロードで長レイテンシのメモリ参照を前倒しし、回転レジスタでループを名前替えなしに重ねる。並列性を引き出す道具立てはコンパイラ向けに整っていた。
- 3.敗因はキャッシュミスの遅延が静的に読めないこと。コンパイル時に決めた束のスケジュールは可変遅延に対応できず、実行時に依存を解く動的OoOがバイナリ互換性も含めて勝った。
並列性を「誰が」見つけるか
命令レベル並列(ILP)を引き出すには、独立した命令を見つけて同時に実行ユニットへ流し込む必要があります。問題は、その依存解析と並べ替えを実行時にハードウェアが行うか、コンパイル時にソフトウェアが行うかです。アウトオブオーダ実行はハードウェアに任せる道で、リザベーションステーションやリオーダバッファが毎サイクル依存を解きます。これは強力ですが、タグ照合の比較器やレジスタリネーミング回路が面積と電力を食い、命令ウィンドウを広げるほどコストが非線形に膨らみます。
VLIW(Very Long Instruction Word)はこの重い回路を丸ごと省く発想です。並列に実行できる命令の組み合わせをコンパイラが事前に決めて固定幅の長い命令語に詰め込み、ハードウェアはそれをそのまま各ユニットへ配るだけにします。動的スケジューリングを捨て、その分のトランジスタを実行ユニットやキャッシュに回せる――これが静的スケジューリングの基本的な設計判断です。
| 観点 | 動的(OoO) | 静的(VLIW/EPIC) |
|---|---|---|
| 依存解析 | 実行時にハードウェア | コンパイル時にソフトウェア |
| 並べ替え | 実行ユニット側で順不同実行 | コンパイラが束に固定 |
| 回路コスト | RS・ROB・リネームが重い | 発行ロジックは単純 |
| 可変遅延 | 実行時に吸収できる | 静的に読めず脆い |
| 互換性 | 同一ISAで世代継承 | 幅変更でバイナリ非互換になりやすい |
命令束 ─ 並列性を語に書き込む
VLIWでは、同時発行する複数の演算を1つの長い命令語にパックします。Intel/HPのEPIC(Explicitly Parallel Instruction Computing)、その実装であるItanium(IA-64)では、これをbundle(命令束)と呼びます。1束は128ビットで、41ビットの命令スロットを3つと、5ビットのテンプレートから成ります。
Itaniumのbundle(128ビット)
[ instruction slot 2 | instruction slot 1 | instruction slot 0 | template ]
41bit 41bit 41bit 5bit
テンプレートは各スロットがどの実行ユニット型(整数・メモリ・浮動小数点・分岐)向けかを示し、さらにstop bitで「ここまでが並列実行可能なグループ」という境界を表します。stop bitで区切られた命令グループ内に依存が無いことをコンパイラが保証し、ハードウェアはその保証を信じてグループ内を同時発行します。純粋なVLIWが「束の中身は必ず並列」と固定幅を強制したのに対し、EPICはstop bitで並列の範囲を可変に示せる点が緩和されています。
古典的VLIWは束の幅を実装のユニット数に固定するため、ユニット構成を変えると過去のバイナリが動きません(コード非互換)。EPICはstop bitとテンプレートで並列性を相対的に表現し、加えて述語・投機・回転レジスタといった「コンパイラが並列性を引き出すための道具」を体系的にISAへ組み込んだ拡張版と捉えると整理しやすいです。
分岐を消す述語実行
分岐はパイプラインを乱す最大要因で、外せばフラッシュで多数サイクルを失います。動的方式は分岐予測で投機的に走りますが、EPICは別の手を取ります。**述語実行(predication)**です。
各命令に1ビットの述語レジスタを付け、その述語が真のときだけ結果を確定し、偽なら何もしなかったことにします。if (cond) A; else B; を分岐で書く代わりに、cond を述語 p1、その否定を p2 に求め、A を p1 付き、B を p2 付きで両方とも無条件にフェッチ・実行します。
p1, p2 = cmp(cond) ; cond を述語へ(真偽を一対で生成)
(p1) A ; p1 が真なら結果を確定
(p2) B ; p2 が真なら結果を確定
分岐が消えるので予測ミスのペナルティが無くなり、両側を並列に流せます。代償は、結局どちらか一方は捨てられる演算であり、実行資源を無駄に使う点です。条件が偏っていて予測が当たりやすい分岐ほど、述語化はかえって損になります。
投機ロードで長レイテンシを前倒し
メモリ参照は完了まで数十〜数百サイクルかかります。並列性を稼ぐには、値が要る地点よりずっと早くロードを始めたい。しかし安易に前倒しすると、本来は実行されないパスのロードが例外(不正アドレス参照など)を起こしたり、間に挟まる書き込みと順序が狂ったりします。EPICは2種類の投機でこれを安全に行います。
| 種類 | 前倒しする壁 | 仕組み |
|---|---|---|
| 制御投機(ld.s) | 分岐より前へロードを移動 | 例外を即発生させず例外トークンをレジスタへ記録し、使用時の chk.s で検証 |
| データ投機(ld.a) | 先行ストアより前へロードを移動 | ALAT表にロード先を登録し、ストアと衝突したか chk.a で照合・必要なら再実行 |
ld.s(control speculation)はロードを分岐より上へ動かし、もし不正参照でも即座に落とさず遅延例外トークンをレジスタに残します。値を実際に使う直前に置いた chk.s が、そのトークンを見て初めて例外を起こすか復旧コードへ飛びます。ld.a(data speculation)はロードを先行する未確定ストアより上へ動かし、ロード先アドレスを**ALAT(Advanced Load Address Table)**に登録します。後続の chk.a がストアとの衝突を照合し、衝突していれば再ロードします。いずれも「投機的に前倒し、使用直前に検証」という構図で、OoOがハードウェアで暗黙にやることをコンパイラが明示的に組み立てる形です。
回転レジスタでソフトウェアパイプライン
ループを高速化する常套手段がソフトウェアパイプライニングで、複数の繰り返しの異なる段を重ねて同時に走らせます。素朴にやると各繰り返しが同じレジスタ名を使うため、繰り返し間で名前が衝突します(WAR/WAW依存)。
EPICは回転レジスタ(rotating register)でこれを解きます。ループの1反復ごとに、レジスタ番号への参照を物理レジスタへ写す基点を1つずらします。同じ命令の r32 が反復ごとに別の物理レジスタを指すので、コードを展開(アンロール)して名前を手で振り直さなくても、繰り返し間の偽の依存が自動的に消えます。これはハードウェアのレジスタリネーミングがやる仕事を、ループ用に特化した専用機構で軽く実現する設計です。
述語実行・投機ロード・回転レジスタは、いずれも「動的OoOがハードウェアで暗黙に行う最適化(分岐の隠蔽・ロードの前倒し・リネーミング)を、コンパイラが明示的に表現できるISA機能として外出しする」という同じ思想で貫かれています。並列性を引き出す道具立て自体は、よく設計されていました。
なぜ動的OoOに敗れたか
道具は揃っていたのに、Itaniumは市場で動的OoOに敗れました。理由は原理的なもので、大きく3つに整理できます。
第一に、可変メモリ遅延に静的スケジューリングが脆いこと。コンパイラはコンパイル時にスケジュールを固定しますが、ロードがL1キャッシュに当たるか主記憶まで行くかは実行時のキャッシュ状態次第で、数サイクルから数百サイクルまで桁が変わります。キャッシュの原理上、この遅延は入力データや並行プロセスに依存し静的には読めません。最悪値を仮定して束を組めば命令束に空(NOP)が増えてコード密度が落ち、楽観値で組めばミス時に全体が止まります。OoOは同じ状況でも、待っている間に独立命令を実行時に拾えるので遅延を吸収できます。これが決定的な差でした。
第二に、コード生成の難しさとコード膨張。十分なILPを束に詰めるには高度な大域スケジューリングが要り、コンパイラ技術が想定ほど成熟しませんでした。並列性が足りない束はNOPで埋まり、命令キャッシュを圧迫しました。
第三に、バイナリ互換性。ISAの設計思想で見たとおり、x86は内部実装を変えても同じバイナリが動き続ける互換性を武器に世代を重ねました。VLIW寄りの設計は実装のユニット構成が透けて見えやすく、再コンパイルなしの世代継承が苦手でした。並列性を「実装の内部事情」としてマイクロアーキテクチャに隠したOoOが、互換性の面でも有利だったわけです。
EPICは汎用CPUでは敗れましたが、静的スケジューリングという考え方そのものが間違いだったわけではありません。遅延が予測可能で電力制約が厳しいDSPや、コンパイラが全体を掌握できるGPUのシェーダ、組込みのアクセラレータでは、VLIW的な明示並列が今も有効です。鍵は「メモリ遅延が静的に読めるか」で、読める領域では動的スケジューラの回路コストを省ける利点が活きます。
「VLIW/EPICは並列スケジューリングをコンパイラへ移す静的方式」「bundle(命令束)+stop bitで並列範囲を明示」「述語実行で分岐を消し、投機ロード(ld.s/ld.a)で長レイテンシを前倒し、回転レジスタでソフトウェアパイプライン」「敗因は可変メモリ遅延を静的に吸収できないこと+コード膨張+互換性で、実行時に依存を解く動的OoOが勝った」の4点を押さえましょう。
まとめ
- VLIW/EPICは命令レベル並列の発見をハードウェアからコンパイラへ移し、固定幅のbundleに独立演算を詰めて同時発行する静的スケジューリング方式。動的スケジューラの重い回路を省ける。
- Itanium(IA-64)はstop bit付きの128ビットbundleで並列範囲を示し、述語実行・投機ロード・回転レジスタで分岐・長レイテンシ・ループ間依存に対処した。道具立て自体は一貫して練られていた。
- 敗因はキャッシュミスの遅延が静的に読めず、固定スケジュールが可変メモリ遅延を吸収できないこと。加えてコード膨張とバイナリ互換性の弱さが重なり、実行時に依存を解く動的OoOに敗れた。
- 静的スケジューリングは遅延が予測可能なDSP・GPU・組込みアクセラレータでは今も有効で、領域を選べば動的スケジューラの回路コストを省ける利点が活きる。
CPU/メモリ/ディスク Article
VLIWとEPICアーキテクチャ ─ 静的スケジューリングの設計判断を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
VLIW
比較で見る軸
難易度: advanced / カテゴリ: CPU/メモリ/ディスク / タグ数: 6
導入後に効く点
述語実行で分岐を消し、投機ロードで長レイテンシのメモリ参照を前倒しし、回転レジスタでループを名前替えなしに重ねる。並列性を引き出す道具立てはコンパイラ向けに整っていた。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- CPU/メモリ/ディスク
- タグ数
- 6
判断チェックリスト
- 自社の用途が「VLIW / EPIC」に近いか確認する。
- 強みである「VLIW/EPICは命令の並列スケジューリングをハードウェアからコンパイラへ移し、固定幅の命令束に独立演算を詰めて同時発行する。配線と電力を食う動的スケジューラを省ける。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。