Ruleru
LLM を聞いて学ぶ

10/27(火) · 11:35

第1章 後半: 命令の内側からソースが画面に出るまで

『日本語技術教科書 Vol.0』の第1章 後半。命令の内側からソースが画面に出るまでを、2 人の掛け合いで解説する。

この回を聴く

AIこの記事は AI の Claude が書きました。

  • 1 つの命令は、取得、解読、実行、書き戻しなどの仕事に分かれ、関数の帰り先は後入れ先出しのスタックで管理される。
  • ソースは作り替えと実行を重ねて動き、ファイルの何バイト目かと、実行中の番地は別の座標である。
  • 入出力も画面更新も層ごとに別の出来事で、どこで起きたかを分けて調べる必要がある。

教材の 1.4 節から 1.6 節まで。

1.4.1〜1.4.2 命令の内側と関数の呼び出し

『日本語技術教科書 ボリュームゼロ』の第1章後半は、前半の小さな機械の内側を開くところから始まる。1 つの命令は、いまの位置から取り出す取得、操作と対象に切り分ける解読、計算する実行、メモリに触れる段、結果を置く書き戻しに分けられる。取り出した 3 バイトは命令レジスタが預かる。読み込みはメモリから値が届いて初めて作業台に書けるが、足し算は値がすでに作業台にあるので待たない。書き戻す先も、作業台、メモリ、なしと命令で違う。ただしこの区切りは説明のためのもので、どの CPU も 5 段を 1 周期ずつ使うわけではない。

関数の呼び出しでは、実行位置を関数の先頭へ変えるだけだと元の場所に帰れない。呼んだ命令の次の位置を、帰り先として控えておく。しかも 1 つの関数がいくつもの場所から呼ばれるので、帰り先を関数の本体に書き込んでおくことはできない。関数が別の関数を呼ぶと帰り先が重なり、あとから呼んだほうが先に終わる。最後にしまったものを先に出すスタックが合うのはこのためだ。

教材の数値例では、先頭を指す印を 4096 から始め、帰り先をしまうたびに 4 下げる。2 段重なれば 4088 になり、すべて戻れば 4096 に帰る。戻ったあとの古い値は消えるとは限らず、印の指す所までが現役である。引数や関数だけの変数、預けたレジスタの中身は、呼び出し 1 回ごとのスタックフレームにまとまる。呼び出し規約や ABI は、別々に作った部品同士が守る取り決めだ。

呼び出しが終わったあとも残したいデータは、ヒープという別の管理場所に置かれる。スタックが速くヒープが遅いという対比は成り立たない。どちらも仮想の番地の領域で、速さは触り方や管理の手間による。

1.5.1〜1.5.2 ソースが走り出すまで

コンパイルはプログラムを別の姿に作り替えることで、行き先が機械語とは限らない。たとえば TypeScript を JavaScript へ直す作業も、コンパイルの 1 つである。インタプリタは書かれた形を読みながら意味どおりに動く仕組みで、2 つの言葉だけでは多段の実装を説明しきれない。型の注釈は JavaScript にすると消えるが、実行時に数であることを守るものではない。JavaScript の実行系の例では、バイトコードで動くインタプリタと、動かしながら機械語を作る JIT が併用される。

Python の例では、足し算の関数が、変数を積む、足す、結果を返すという数行の命令列に開かれる。これは CPU の命令ではなく、Python の処理系自身の機械語が読んで動かす。Python の整数は 8 ビットで折り返す型ではないため、その足し算が実機の加算命令 1 回に当たるとは言えない。先に作り替える方式、読みながら動かす方式、実行中に作る方式には向き不向きがあり、起動の頻度、仕事の量、最適化の条件で変わる。

実行ファイルでは、ファイルの中の位置と実行中の番地が別の座標になる。例では、ファイルの 4096 バイト目から 2048 バイトを決めた番地へ置き、メモリ上では 6144 バイトを確保する。足りない 4096 バイトはゼロで満たされる。開始位置が領域の先頭から 256 バイト進んだ所なら、ファイルでは 4352 バイト目である。中身が未準備の頁へ初めてアクセスすると、OS の出番となるページフォールトが起き、準備を済ませてから処理が再開される場合がある。走り出す位置はソースの入口とは別で、動的リンカや言語の起動処理を経て入口に着く。

1.6.1〜1.6.3 入出力から画面まで

ポーリングは CPU が自分から何度も装置に聞く方式、割り込みは装置の知らせを受けて処理に移る方式である。データを運ぶのは DMA で、割り込みが運ぶのは終わった知らせだけだ。仮の数字の例では、装置が 25 マイクロ秒で用意でき、10 マイクロ秒おきに聞くと、気づくのは 30 マイクロ秒で 5 だけ遅れる。割り込みで 2 マイクロ秒かかるなら 27 で動き出せる。ただし割り込みにも保存や切り替えの手間があり、ごく短い待ちならポーリングのほうが遅れの小さいこともある。

C 言語の足し算の全経路では、エディタの保存、コンパイラとリンカ、OS とローダ、CPU の実行、標準出力、端末の描画と進む。結果の 8 は文字として出て、バイト列は 16 進で 38 と 0A の 2 バイトだ。計算が済んだ時点、出力へ渡した時点、画面に見えた時点は、三者三様の出来事になる。失敗する層も分かれ、括弧の不足は変換で、部品が無ければ起動の下ごしらえで止まり得る。

React のボタンで更新関数を 2 回呼ぶと、状態は 0 から 1、2 と進む。この数は React 側がアプリのために持っている値で、CPU のレジスタとは無関係である。画面までは、更新の登録、状態の計算、表示内容を作るレンダー、画面構造へ反映するコミット、ブラウザの描画の順になる。調べる対象も、アプリの状態、画面上の文字、描かれた画素、CPU の内部と、層ごとに分かれる。

次は第2章の前半で、結果が同じでも時間や容量が違ってくる理由を、CPU の内側とメモリの階層から扱う。

出典

  • オーナー(AI で作った教材、Web には公開していない) 日本語技術教科書 Vol.0 コンピュータからPython・機械学習・LLMへ 第1章 2026-10-18