『コンピュータの構成と設計 MIPS Edition 第 6 版 上』 はコンピュータ・アーキテクチャの教科書です.
2 名の著者パターソン&ヘネシーの名前を取ってパタへネという愛称で知られています. ヘネパタという紛らわしい愛称が付けられている『コンピュータ・アーキテクチャ』はより上級者向けの内容です.

本書はソフトウェアとハードウェアの境界付近についての本です. コンピュータの中核的な仕組みを説明し, プログラムを書く上でどうやってハードウェアを活用すればよいかという視点で語られます. コンピュータの中身を知りたい方におすすめです.

Moore の法則の終わり

プロセッサは数百ものトランジスタが搭載された集積回路によって実現されている. トランジスタを始めとする半導体素子の材料になるのはシリコンという砂に含まれている物質だ. 円柱状のシリコン結晶を 0.1mm ほどに薄くスライスしたウェハ (wafer) を格子状にカットすると, 小さなチップができる.

チップ一つ当たりのトランジスタ数が 2 年で倍増するという, Intel の創始者の一人である Gordon Moore の予想「Moore の法則」は 50 年間に渡って正しかった. しかしいつまでも指数的な成長が続くわけではない. 消費電力の増加とともに発熱が増え, ついには冷却性能の限界を迎えたのだ. ここに来てプロセッサ開発者は方針転換を余儀なくされた. 一つのプロセッサの性能が頭打ちとなったので, 一つの CPU に複数のプロセッサを搭載することにしたのである.
マルチコア CPU の性能を引き出すにはプログラムの努力が欠かせない. 現代は, ソフトウェアエンジニアもハードのことを考えなければならない時代なのである.

MIPS について

本書で取り扱われる MIPS という命令セットは, 命令数を抑えシンプルさを重視して設計された. フォーマットが単純であれば規則性が保たれ, 回路の実装が容易となる. そして単純な回路は消費電力を抑えらる.
スマートフォンの時代 (ポスト PC 時代) において, 消費電力は命令セットの良し悪しを決める鍵となった. MIPS と同様の思想を持って設計された ARMv8 や RISC-V が脚光を浴びるのは自然な流れであった.

MIPS のフォーマットはシンプルである. まず, 全ての命令が 32bit の固定幅を持つ. 算術/論理演算は全て二項演算で, 2 つのオペランドと 1 つの結果格納先を指定する. オペランドにメモリ上の値を指定することはできず, 事前にメモリ上の値をレジスタにロードしておかなければならない.
二項演算しかないということは NOT のような基本的な単項演算が存在しないということだ. MIPS では代わりに NOR(=Not Or) を用いて NOT を計算する (x NOR 0 = NOT x). 基本的な演算を削除する選択をしてまでもシンプルさを優先するのだ.
MISP に対して x86 は複雑である. 命令長からして 7-15bit と可変だし, 命令数は 1400 以上もある. 40 年以上に渡る開発の歴史の中で互換性を保ちつつ進歩を続けて来たのは驚異的だが, 実態としては複雑であるというしかないだろう. 見ようによっては混沌の中にも秩序があるのかも知れないが, とにかく一見すると複雑そうに見えるのは確かである.

コンピュータの内側

コンピュータの内部で数がどのように表されるか, 計算はどうやって行われるのか. ブラックボックスの中を除くのが本書の趣旨だ.
2 の補数や浮動小数点はコンピュータ開発に携わるものの教養なのだろう. 初めは戸惑ったはずだが, どの教科書にも書いてあるので, 今ではすっかりお馴染みとなった.

2 の補数, 浮動小数点, 文字. データは全てビット列で表される. ビット列をどう解釈するかによって意味が変わるのだ. 同じビット列が符号付き整数の +1131443456 だったり, ヌル終端文字列の “Cpu” であったりする. 全てがビット列であるのは命令も同じだ. つまり, プログラム自体もビット列で表されるデータなのである.
実行すべきプログラムをメモリに展開して, 一つずつ読み取る. プログラムを数値や文字と言ったデータと同様に扱えるプログラム内蔵方式のおかげで, コンピュータはプログラムを容易に切り替えることが可能となり, 飛躍的な汎用性を獲得した. と言っても, プログラム内蔵方式ではないコンピュータを見たことがないので, いまいちその恩恵に対する実感が沸かないのだが.

プロセッサの実装

OS が実行ファイルの内容をメモリに展開するところからプログラムは始まる.
命令メモリから命令をフェッチする. 命令をデコードし, 命令の種類やオペランドを取り出す. レジスタから値を読み出し, ALU で演算を行う. 最後に演算結果をレジスタやメモリに書き込む. プロセッサは大まかにこの繰り返しだ.
一つ一つの処理を実装する回路はそれほど複雑ではないので, 全てを組み合わせてできるプロセッサ全体も意外なほど単純である. 単純さを重視した MIPS の設計のおかげであろう.

処理を順番にこなす回路は分かりやすいが, 無駄が多くて遅い. 例えば ALU で計算を行っている間, 命令をフェッチする機構は暇をしている. そこで各ステップでは結果を次のステップに渡すと, 全体が終わるのを待つのではなくすぐに次の命令の処理に取り掛かる.
バケツリレーに例えられるパイプライン処理はプロセッサの根幹を成すアイデアだ. MIPS はパイプライン処理を念頭に置いて設計された. パイプライン処理によって各ステップを平行に進めると, 理想的にはステップ数倍のスループット向上が見込める. 全てのステップが満たされているとき, プロセッサはある時点でステップ数個の命令を同時に処理していると見なすことができるからだ.

しかし, パイプラインのスムーズな流れを妨げる厄介なハザードがある. 前の命令の結果に依存した計算を行う命令や, 計算が終わるまで次の命令が確定しない条件分岐である.
命令に依存関係があるとき, 結果をレジスタやメモリに書き戻すのを待たずに次のステップにデータを送るフォワーディングが有効だ. 更に高度な手法として, コンパイル時や実行時に命令の順番を変えて依存関係による待ち時間を減らす静的/動的パイプラインスケジューリングという手法もある. しかし当然の疑問として, 実行時に順序を変えると元のプログラムと結果が変わってしまうのではないだろうかという気がする. 実は最初と最後の順番は変えず, 途中の実行だけ順番を変えるのだ. イン・オーダー発行 (順番通りに命令をフェッチする), アウト・オブ・オーダー実行 (順番を変えて実行する), イン・オーダー確定 (順番通りに結果をレジスタやメモリに書き込む). これによって, 外から見た結果が元のプログラムと同じに保たれる.
条件分岐では分岐先が確定するのを待たずに, 分岐先を予想して先に実行を始めてしまう投機的実行で待ち時間を減らす. 予想が外れたときは計算が無駄になるから, 高い精度で分岐するか否かを予想することが肝心だ.
Intel Core i7 6700 での予想成功率はどのくらいだろう?50% そこそこだろうか. 実際は, なんと驚異の 97.7%(SPECCPUint2006 のベンチマーク結果による). これほどまでに高い精度を実現している秘訣の一つは動的分岐予想だ. 分岐命令ごとに前回の分岐成否を記録しておくのだ. 実際にこの方法で for ループでは最初と最後意外の予想を当てることができるから, シンプルながら強力な方法である.

結び

以前読んだコンピュータ・アーキテクチャの教科書『コンピュータ・システム』と共通する部分が多いと感じました. 既知の内容は多かったものの、同じ事柄に対する別の説明を聞くことで理解を深めることができると実感したので, 短いスパンで同分野の本を読むのは悪くないと思いました.

改訂を繰り返して第 6 版となった本書は時代を反映してます. スマートフォンが一年で 1500 万代 (=PC の 6 倍) も生産されるようになった現代において, プロセッサには消費電力を抑えることが強く求められるようになったことが随所で説明されます.
練習問題で興味深かったのは Google が考案した 16bit 浮動小数点フォーマット「Brain Float 16」の消費電力を計算するものです. 乗算器の消費電力は入力サイズの二乗に比例するらしく, 仮数部が 7bit(= 省略された先頭の 1 を加えて 8bit 幅) である Brain Float 16 は IEEE の 16bit フォーマットに比べて約半分(8^2 : 11^2), 32bit のフォーマットに比べて 11% 程度(8^2 : 24^2)の消費電力で済みます. 機械学習には 16bit の精度で十分らしいですから, Brain Float 16 が考案され広く使われるようになるものうなずけます.

今回読んだのは上巻ですが, 下巻はメモリ階層, ソフトウェアレベルでの並行プログラミングといったトピックが扱われます. 続けて読もうと思います.