BlueTechNote

学んだことをつらつらと

WEB系エンジニアが『コンピュータシステムの理論と実装』を完走した感想

『コンピュータシステムの理論と実装』という書籍を最後までやり切ったので、感想を書いておこうと思います。加えてこの本のおすすめポイントやこれからやる人に向けてアドバイスなども。

約三週間ほどぶっ続けでやって終わらせました。(トータルで130〜150時間ぐらいかかった気がする。大変だった・・・。)

自分が実装したコードは以下のレポジトリに公開しておきます。

github.com

この本は何?

海外では通称「nand2tetris」と呼ばれている本で、Nand回路一つから始めて自分の手でコンピューターを作り上げ、最終的には作り上げたコンピューター上でtetrisを動かすところまでやるという、物凄い本です。

Nand回路というのはAndの反対の回路(Not And = Nand)です。論理演算で false && false の時のみ trueになり、それ以外は true になる回路のことです。 この一つの回路だけでなんとコンピューターが作れちゃいます!

具体的にはまずALU(演算装置)、メモリ、CPUを順に組み上げてハードウェアを作成し、その後にアセンブラVM言語変換器、コンパイラ、OSを順に作ることで、本書独自のHackというプログラミング言語を、自らが作成したコンピューター上で実行できるようにして最後はテトリスを動かすとこまでやります。

書籍の裏に書いてあるわかりやすい説明も載せておきます。

コンピュータを理解するための最善の方法はゼロからコンピュータを作ることです。 コンピュータの構成要素は、ハードウェア、ソフトウェア、コンパイラ、OS に大別できます。 本書では、これらコンピュータの構成要素をひとつずつ組み立て ます。 具体的には、NAND という電子素子からスタートし、論理ゲート、加算器、CPU を設計します。 そして、オペレーティングシステムコンパイラ、バーチャルマシンなどを実装しコンピュータを完成させて、最後にその上でアプリケーシ ョン(テトリスなど)を動作させます。 実行環境は JavaMacWindowsLinux で動作)。

なぜやろうと思ったか

シンプルに低レイヤーへの好奇心からです。

今までWeb系のソフトウェアエンジニアとして既存の技術(Ruby on RailsやGo, React, Vue)などを使ってアプリケーションを開発してきたのですが、自分が書いたコードがどうやって0と1しか解釈できないコンピューター上で実行されているのか全くイメージが湧きませんでした。

そこで、コンピューターがプログラムを解釈する仕組みを理解できる本を探したところ、この本をおすすめする人が多く、Amazon等のレビューも高かったのでやることに決めました。

最後までやった感想

総じて素晴らしい本でした!! 自分で作ったコンピューターでプログラムが動かせたときは大感動もんでしたね!

今までコンピューターが動く仕組みが全くわからなかったのですが、この本をやり終えた今なら簡単なコンピューターぐらいなら自分で作れちゃうかも??ぐらいには思えるようになりました!

感想は山ほどあるのですが、その中でも特に強く感じたことを語っていこうと思います。

コンピューターの仕組みに感動した

この本ではたった一つのNand回路から全てが始まります。この回路をもとにして以下のようにどんどんより大きなものを作っていきます。

  • 1ビットNand回路→1ビットAnd, Or, Not回路→16ビットAnd, Or, Not回路→マルチプレクサ→ALU(演算装置)

同様にして、メモリもフリップフロップという基本部品をもとにして作成し、最後はALUとメモリなどを組み合わせてCPUを作り上げます。

小さなものを少しずつ重ね合わせていくことで巨大のものを作り上げるというのはソフトウェアの常識ではあるのですが、それ実際に自分でやってみる事でその凄さが実感できました。

最初は1ビット分の簡単な論理演算しかできなかったのに、最終的には大量の計算を高速にできるようになりました。

このような小さなことの積み重ねが、最後にはまるで魔法のように見えるということに、ただただ感動しました。

抽象化の大切さを知った

この本では基本的に以前の章で作ったものを利用して現在の章の実装を行います。また、この本はボトムアップで進んでいくので、後半になればなるほどレイヤーが上がっていきます。

つまり、高レイヤーはより低レイヤーの部品を利用するのですが、その時にその部品が高度に抽象化されているので、高レイヤー側は低レイヤーの仕様などを気にせず実装に集中することができます。

この本も全体的にそのような高度な抽象化の積み重ねになっています。特にコンパイラを作る章では、その下のVM変換器が利用できるおかげでハードウェアの仕様詳細を気にせず実装できるという利点をとても感じることができました。

現代のソフトウェアの世界も高度な抽象化の積み重ねでできており、そのおかげでWebアプリケーションを作成する自分のようなエンジニアは、低レイヤーの世界をあまり意識せずに実装できているのだということが理解できました。

ホント、先人たちには感謝してもしきれません・・・。

この本の内容は始まりに過ぎない

この本では確かにコンピューターを作り上げるのですが、言ってしまうと大変しょぼいコンピューターです(現在のものと比べると)。

16ビットまでしか扱えないし、通信できないからインターネットには繋げないし、スクリーンは512×216ピクセルで白黒のみだし、Hackという言語も言語仕様がしょぼいし・・(言い出したらキリがない・・)

あと本書では最適化(パフォーマンスとか軽量化とか)については一切考慮してません。

それに比べて今のコンピューターは数億倍すごいです。自分が今使ってるM1のMacbookなんてネットし放題だし、音楽も動画も見れるし、いろんなアプリ立ち上げるし、凄すぎますね(どうやってこんなの作ってるんだ・・・)

この本で実際にコンピューターを自分の手で作ることによって、今のコンピューターがいかに凄いかがわかりました。このような素晴らしいコンピューターを作ってくれた方々にもっと感謝しながら生きていこうと思いました。

この本の良かった点

ここからはこの本の良かった点を喋ります。評価が高いのも納得の本でした。

演習形式

まずはなによりも各章の終わりにある演習問題ですね。というかこれがこの本のメインです。

この演習問題がめちゃくちゃ身になるし、何よりも楽しいです。(絶対クリアしてやるぞ!!的な?)

この本では各章の構成が決まっていて、解説→仕様の解説→演習問題と実装案の提示→さあ解け!みたいな感じになってます。

答えとかは一切ないのですが、自分で考えて実装できるだけの材料は全部揃えてくれます。なのでそれをもとに自分で考えて実装します。

テストもちゃんと用意されているので、実装→テストを実行する→落ちる→どこが間違ってるかデバッグ→直してまたテスト→全て通った!やった!!、みたいに楽しく進めていけます。

この楽しいっていうのが何よりも良かったです。モチベも保ちやすいし。(逆に演習問題やらずに読むだけの場合は、この本は全然お勧めできないです。やっぱり自分で考えて問題解いてこそなので・・・)

読者への補助が優秀

この本には公式サイト(https://www.nand2tetris.org/)があって、そこで演習をやる際に必要になるツール等が全てダウンロードできます。

特にはコンピューターは実際に物理的に組み上げるわけではなく、ハードウェア記述言語(HDL)というのを使って自分のPC内で仮想的に組み上げるので、そのツールなどが全て用意されてます。

ツールはそれぞれチュートリアルもついてて、使い方等もそれを読めば理解できます。

また、公式サイトには英語ではあるのですが、図などが豊富なスライドなどもありますし、この本を終わった人が作ったプログラムなんかも公開されてます。

これなんか凄いです↓(どうやって作ったんだよ・・)

https://www.youtube.com/watch?v=inFJ5EyOhpM

あとは、自分が作ったものが正しく動作しているかを確かめるためのテストも全て用意してあります。

このテストが全てクリアできることを目指して実装すれば良いので、実装に集中できるように配慮がされてるなーと感じました。

ここらへんのこの本を進めてく上の補助輪的なものがとても優秀だと感じました。

複数の内容を一冊にまとめきったこと

この本は広く浅くではあるのですが、メモリ、CPUなどからコンパイラやOSまで、コンピューターを構成するものが全て1冊でまとまっています。

ある内容について詳しく書いてある本はたくさんがありますが、複数のトピックをそれぞれ最小限に保ちながら一冊にまとめきるのは素晴らしいなと感じました。それがこの本が高評価たる所以かなと思います。

言い換えると、この本はコンピューターの基礎原理を把握するという目的においてはとてもコスパの良い本と言えますね。

逆に微妙だった点

とても良い本なのですが、逆に気になる点も何点かあったので、それも書いておきます。

副作用ありまくりの実装方針

この本では実装案みたいものを毎回提示してくれるのですが、その案通りに実装する副作用ありまくりな実装になります。なので、デバッグするときに状態の変化が追いづらかったですし、テストもちょっと書きづらかった印象です・・。

最近、関数型言語の勉強をしてることもあって、なおのことそこら辺がちょっと気になってしまいました・・。

副作用が気にする方は、この本の実装案をあまり参考にせず、自分で考えて実装したほうがいいかもです。

仕様を確認するために何回も章を跨ぐ

以前の章で出てきた仕様を前提して話が進んでいくので、それを思い出すために何回も章を跨いで確認しながら進めてました。それがちょっとだけ煩わしく感じました・・。

その章で必要になる知識は全てその章だけでまとまってると、よりやりやすい気がしました。(なんか前書きで各章は独立してるからどこからやっても大丈夫とか書いてあったが、そんなわけなくない?笑)

OSの内容は薄い

最後にOSを作りますが、なんちゃってOSです。というかほぼ標準ライブラリです。

割算・乗算・平方根の計算を扱うMathクラスとか文字列を扱うStringクラスとかを作ります。OSっぽいMemoryクラスなども作りますが(このクラスはメモリの確保とか破棄を行います)。

LinuxみたいハイパーすごいOSなどは全然作りません。OSの章はホントおまけみたいな感じです。

なので、OSに関して深く知りたい方はこの本ではなく、他の書籍がおすすめです。(みかん本って言われてるやつとか)

最後に動くのがテトリスじゃない

これが何よりも衝撃でした!!

ここまでテトリスが動かしたくて作ってきたのにテトリスじゃない!!なんだと!???

みたいに皆さん、なります。多分。

最後に動かすのはPongっていう壁当てゲームです。(じゃあ、なんで nand2tetris っていう名前なんだよ・・・)

個人的にはテトリスを動かすことを目指してずっと頑張っていたので、最後に肩透かしを食らったみたいで少しだけ残念でした笑

各章の内容を振り返る

ここからは各章を雑に振り返っていこうと思います。

また、個人的な難易度も一緒に載せていこうと思います。ぜひ参考にしてみてください。

第1章 ブール論理

難易度:★☆☆☆☆

  • 真理値表やブール代数などを初めて知る。
  • NAND回路一つだけが与えられるので、そこからAND, OR, NOT, XOR, MUX, DMUXなどを作った。
  • それぞれの仕様はシンプルで簡単だったが、作る量が多く時間がかかった。

第2章 ブール算術

難易度:★★☆☆☆

  • 符号付き2進数を表現するための2の補数形式や、算術演算は論理演算で表現できることを知った。
  • 半加算器, 全加算器, 加算器, インクリメンタ, ALUを作った。
  • HDLの記述方法がわからずやや苦戦・・。他の人のレポジトリをみることで初めて知った書き方多数。

第3章 順序回路

難易度:★★★☆☆

  • 順序回路とは回路に記憶装置がついたもの。クロックが定期的に信号として入力される。
  • フリップフロップは最も基本的な順序回路。遅延して値を記憶する仕組みがよくわからず、調べまくって理解するのに時間がかかった。t と t-1 の時間の遅延を理解するのが大切。
  • D 型フリップフロップDFF)だけが与えられ、レジスタ, メモリ, カウンタを作った。

第4章 機械語

難易度:★★☆☆☆

第5章 コンピュータアーキテクチャ

難易度:★★★★★

  • 今まで作成したものを全て使ってノイマンアーキテクチャのコンピューターを組み上げる。ここまでがハードウェアの世界。
  • まずはALU、レジスタ、カウンタを使ってCPUを作った。仕様と図を常に見ながら実装を進めた。
  • うまく場合分けする方法がわからず詰まった。調べまくって、マルチプレクサをうまいこと使って場合分けをすることが分かってからスムーズにできた。
  • CPUができたらあとはメモリ(RAMとROM)とあらかじめ与えられたキーボードとスクリーンを繋げて、コンピューターを完成。
  • コンピューターが出来上がったので、あとは好きなプログラムを読み込ませることで好きな振る舞いをさせることができるようになった。感動。

第6章 アセンブラ

難易度:★★★☆☆

  • ここからはソフトウェアの世界。
  • 4章で説明されたアセンブリ言語機械語に翻訳するプログラムを書く。仕様は4章でほぼ説明済み。
  • アセンブラの仕様はそこまで難しくない。
  • 好きな言語で実装する。Go言語でテストも書きつつ実装したが、あまり慣れてなかったため時間がめちゃくちゃかかった。

7章 VM #1 スタック操作

難易度:★★★☆☆

  • 高水準言語からアセンブリ言語に一気にコンパイルするのではなく、間に一つ中間言語VM言語)を挟む。コンパイラのバックエンド部分を作り上げる。
  • スタックとヒープを初めて知る。算術コマンドとメモリアクセスコマンドを実装する。
  • 翻訳後のアセンブリ言語の記述がどこにもなかったので、翻訳後にどういう記述になってほしいかを考える→実装っていう順番でやってた。
  • ひとつ前の章でテストの量で死にそうになってたので、必要最低限のテストだけ書くようにして本書付属のテストに頼ることにした。

8章 VM #2 プログラム制御

難易度:★★☆☆☆

  • goto文やif文、関数の宣言と呼び出しなどを実装する。
  • 関数の再帰的な呼び出しは全てスタック処理で実現できることを知った。これはシンプルでとても強力だと思った(著者もその凄さを絶賛していた)。
  • 翻訳後の記述を考えて、それをうまいこと構造化できそうになかったので、そのままベタ書きしまくった。その結果瞬殺で終わった。(これでよかったのかは謎)

9章 高水準言語

難易度:★☆☆☆☆

  • Hackという言語の使用説明。簡単なアプリの実装例もついている。
  • Hack言語で簡単なアプリを作ってみろという課題だったが、めんどかったので実装例だけ軽く読んで終了。

10章 コンパイラ #1 構文解析

難易度:★★★★☆

  • 字句解析、文脈自由文法、構文木再帰降下アルゴリズムなどを知る。
  • 解析結果はXMLファイルに出力する構文解析器を作った。解析は再帰降下アルゴリズムを使って再帰的に行った。
  • Hack言語の文法を確認しながら地道に作った。時間はかかるが文法通りに実装していき、再起の仕組みさえ間違ってなければ完成できるはず。

11章 コンパイラ #2 コード生成

難易度:★★★★★

  • 最後の山場。前章では解析結果をXMLファイルに出力していたが、今度はその解析結果をVM言語に逐一変換する。
  • 仕様は分かりきっているが、それをコードに変換していくのはめちゃくちゃ大変だと実感した。
  • テストが落ちるたびに落ちたとこを直していくスタイルで根性で最後まで作り切った。自分で作ったコンパイラが適切なエラーメッセージを吐かないためデバッグが地獄だった。エラーメッセージは偉大。
  • 終盤はもうコードがめちゃくちゃで完成したらなんでもいいやの精神でやってた。もう一度作るならば設計をしっかりやりたい。

12章 

難易度:★★★★★

  • 標準ライブラリを作っていく章だが、量が多い&アルゴリズムだらけで辛かった・・。アルゴリズムを理解するのにめちゃくちゃ時間を要した。
  • 連結リストを使ったメモリの破棄と再利用の実装は大変だったがすごく勉強になった。
  • 量がとんでもないため、ちょくちょくカンニングしながらやってた。
  • あと、自分が作ったコンパイラコンパイルしようとするも、何行目の文法がどう間違っているのか何も教えてくれないため、ゴミだった。本書付属のコンパイラはそこら辺全部表示してくれる。せっかく作ったのになんだか悲しくなった・・。

これからやる人に向けてのアドバイス

最後にこれからこの本に挑戦して見たい人に向けて、ちょこっとアドバイスを残しておこうと思います。

仕様の理解に時間をかけること

これが一番大事です。実装を始める前に紙に書き出すとかして、仕様を完全理解できるように心がけるといいと思います。じゃないと実装に入ったときにわかんなくなって困ります・・。

この本は仕様がすごく良く纏まってるので、都度見返して理解することを重視しましょう。

説明不足なとこはググって補うこと

この本はたまに説明不足なとこがあったりします・・。よくわかんないとこがあったらググってよりわかりやすい記事を見て理解した方がいいと思います。

2分木探索の後行順探索方法に関する説明はひとつもなかったので、以下の記事とかで理解しました。(このサイトはコンピューターサイエンスの知識が分かりやすく纏まっていておすすめです)

www.momoyama-usagi.com

自分は12章のメモリの破棄と再利用の部分が何回読んでも理解できませんでした・・。(オーバーフローすることの考慮について書いてなかったり、ヒープ再利用時の図がなかったり・・)。

そこで以下の公式のスライドをみると、図が豊富で、かつ本の中では説明してなかったこともたくさん書いてあって一発で理解できました。

drive.google.com

なので、ん?どういうこと?ってなった時は公式のスライドを見たりするとわかるかもしれません。

下記ページの各章の真ん中のアイコン(何かを教えている人のアイコン)をクリックすると見られます。 https://www.nand2tetris.org/course

慣れた言語でやると早いかも

この本では6章以降は好きなプログラミング言語を使って実装していきます。

自分はあまり慣れてるとは言えないGo言語で実装して時間が余計にかかったような部分があるので、普段から使い慣れている言語でやると、無駄な時間を食わなくて済むかもです。

勢いと根性で短期でやる

この本は長いし、問題もたくさんあるしで、完走するのはとても大変です。

少しずつコツコツとやるのもいいんですが、あまりに時間かけすぎるとモチベがどんどん下がっていくので、ある程度は短期で一気にやることをお勧めします。

また、テスト書きつつ実装するのも素晴らしいんですが、全部のテストを書こうとすると死ぬので、適度にサボるのも大事かなと思います・・(テスト大事なとこだけ書くとか、他の人の回答ちょっと見るとか)。

とりあえずモチベの維持が最も大変なので、それを維持できるような方法を考えるのが大事ということです!

まとめ

完走するのはとても大変だったのですが、コンピューターの世界が理解できたので最後までやってよかったです!やっていてとても面白く楽しい本なので、全エンジニアにおすすめできる本です!

また、コンパイラを自分でゼロから書く体験ができたのもとても良い体験でした。次はもっとうまく書きたいです。

最後に、この本をやり終えて最も感じるのは今のコンピューターの凄さですね。これはまさに人類の叡智です。

次にやりたいことなど

OSについてはまだまだ理解していないことが多いので、そこら辺を深掘りしていきたいです。

Linuxについて詳しく知りたいので、「Linuxのしくみ」とか「ふつうのLinuxプログラミング」とかの本をやろうかなーと考えてます。

次はOS周りに詳しくなるぞ!