「プログラマーのための CPU 入門」を読んだ

2024-03-31

プログラマーのための CPU 入門 ー CPU は如何にしてソフトウェアを高速に実行するか」を読んだので、本の感想を書きました。

読み始めた経緯

この本を知ったのは、一緒に ISUCON に出た 1 同期が読みたい本として紹介してくれたのがきっかけでした。

「降りられる限界のレイヤがそのエンジニアの力量」という文言と一緒にその本が Twitter で話題になっていたこともあって、輪読してみようという流れで読みました。 ちなみに輪読方法は 良さげな方法 を見つけてくれたので試してみたところ、仲間内 3 人で読むのにとても良い方法でした。

本の概要

詳細は 販売元の紹介ページ を見てもらったほうが良いと思うので、そちらを参照ください。

テーマを数行でまとめるとこんな感じです。

  • 命令実行の密度を上げるための工夫:パイプライン処理、スーパースカラ化、スーパーパイプライン化
  • 命令実行を高速化するための工夫:分岐予測、キャッシュメモリ
  • I/O アクセスが遅い話
  • CPU による命令実行を複雑化させる要因:システムコール、例外、割り込み
  • マルチプロセッサ環境において共有データの整合性を保つための方法

感想

全体を通して

入門としては十分な内容でしたが、CPU 内部のアーキテクチャについては大学の情報科で習うような基礎的な知識を前提としていると感じました(自分が習ったのでそう感じるのかもしれません)。 レジスタ、プログラムカウンタといった単語は詳しい解説もなく普通に出てきますし、第 2 章の段階でパイプラインステージの概念が説明されて以降、1 つの命令を実行するための CPU の基本動作についてはほとんど出てきません。 ALU など CPU 内部の構成については本書の対象外、もっと下のレイヤということだと思います。

それと関係する話ですが、全体を通してハードウェアの具体的な実装・動作まで踏み込んだ説明が多くなく、そこは想像で補いながら読む必要があった印象です。 命令実行のリオーダーの流れ、ベクタテーブル(割り込み発生時に使う、命令流を切り替える先のアドレスが記録されたテーブル)の中身、不可分操作(アトミック操作)が競合しないための仕組み…などなど、概念的な説明はあったのですが、具体的なところは各ハードウェアごとに違うということで詳細な説明がありません。 ただ本書は「入門」という位置づけなので、ボリュームを考えるとちょうど良い説明レベルな気もします。

1 つ良かった点を挙げると、各章にあるアセンブリでの実験により、一般的な CPU に施されている工夫が効果を発揮しているということを実感できました。 工夫が有効な場合と無効な場合とで単純なアセンブリを実行し、プログラムの実行にかかるクロックサイクル数がどれくらい変わるかという実験が、各章最後に書かれています。 実験のため命令を 10 億回繰り返すなど極端な例ではありますが、ソフトウェアの実装の仕方によってパフォーマンスが整数倍レベルで変わるのはなかなか衝撃でした。 高級言語で書かれた実際のソフトウェアに適用する場合はどうすればいいのか、という具体的なところへは、やはり自力で繋げないといけない(そしてケースバイケースで難しい)ですね。

印象に残ったこと

キャッシュの仕組みは様々なところで活用されているんだな、ということを確認できました。 「より高速にアクセスするには?→ キャッシュを使います」という展開が随所に出てきます。 ISUCON でもキャッシュを使う場面が多いですし、どのレイヤでもキャッシュは役に立つ戦略だと知見が広がりました。

また、本書で学んだハードウェアレベルでのキャッシュを活用するために、高級言語で書かれたソフトウェアをチューニングしようとするのは、ハードウェアへの高い解像度が必要だということも理解できました。 正直に言うと自分の実力ではそこまで考えて書くのは難しいですが、すぐに実践できなくても概念レベルで知っておくことは「降りられる限界のレイヤがそのエンジニアの力量」という言葉の通り大事だと思います。

そして読んでみて良かったのは、マルチプロセッサ環境の場合についても説明されている点です。 単一 CPU では起こらない問題が、マルチプロセッサでは起こります。 さらにソフトウェア開発者からはその問題に気づきにくいということで、触りだけでも知れて収穫になりました。

たとえば、複数の CPU が同一アドレスのメモリへアクセスすることによって整合性の担保が必要になり、またそれに伴ってキャッシュミスが起こり、処理速度に悪影響を及ぼすこと(キャッシュコヒーレンスなどの話)は、大学で習ったような気がしないでもないですが、面白い話でした。 単に複数の CPU を使って処理させるより、キャッシュのフル活用を狙って単一 CPU に処理させたほうが良いケースもある、ということも理解できました。 それと関連する話で、ISUCON12 の優勝チームが CPU affinity を調整してスコアを上げていた という話を思い出したのですが、「なぜそれでスコアが上がるのか ≒ 処理が速くなるのか」を納得することもできました。

このように、CPU にソフトウェアを高速に実行させるのは単純なことではなく、ハードウェアレイヤまでの深い理解が必要だということを学びました。

次に読んでみたい本

この本を読み終えた自然な流れとして、CPU で高速に命令を実行するために、OS レイヤでどんな工夫が凝らされているかが気になってきました。

ということで「Go ならわかるシステムプログラミング」を読んでみる予定です。

book

2023 年の振り返りと来年の目標