Island Life

< 子どもは何にも知らないの | つまりあれだ、普段からだだ漏れ抽象化の穴... >

2007/09/14

収束

あちこちで議論して、なかなか自分としては有益だった。だらだら書いてみる。

私の下のエントリの問題のひとつは、 (a) 現場でトラブル発生時に実際に「降りてゆく」必要性とか、計算機を「使いきる」ための 下層からくる物理的制約、という話と、(b) システムを理解してゆくための論理的、 あるいは数学的根拠、という話の2つがごっちゃになっているから、であるようだ。

鵜飼さんとこで 出たように、トラブル発生時に降りてゆく必要という意味では CPUのインストラクションセットアーキテクチャ(ISA)までわかっていればほぼ大丈夫なはずで、 というのは論理回路のバグはたとえ気づいたとしてもソフト屋さんには手の出しようが無い。 (TTLで組んだクロック2MHzとかいう時代ならロジアナやオシロで追いながらジャンパ 飛ばしてって話はあったろうけれど、今のマシンでそこまで追えないし、たとえ 追えたとしてもジャンパひとつ飛ばすのにだって分布定数回路の知識が必要になるから、 「論理回路」の下層まで降りるってことになる。そこはもうハード屋さん、あるいは ファーム屋さんが触るところで、ソフト屋さんの領域ではないと言い切っていいだろう。) ISAの上にはOSやミドルウェア、言語処理系、各種ソフトウェアツール郡が乗り、 抽象化の層を為している。

一方、ソフトウェアを支えている理論的な骨組みを学ぶ、という意味では、個々の CPUのISAはあまり問題ではなくて、抽象機械としてのチューリングマシンやラムダ計算、 それを実装するノイマンアーキテクチャのモデル (仮想的なCPUでいい)、 それを実現するための論理回路 (組み合わせ回路と順序回路)、って具合の話だ。 こちらは、現実の計算機に沿ってはいるものの、特定の面だけに注目して 他を捨象したモデルを取り扱う。こちらも抽象化の層があり、 下層のモデルは上層のモデルの振る舞いをある程度定量的に議論するために 必要だ。Knuthが "The Art of Computer Programming" で仮想機械語MIXを 導入したのは、アルゴリズムの計算量をO(n)などの見積よりもより細かく (係数レベルまで)算出するためにCPUのモデルが必要だったからだ。 もちろんMIXのモデルは現実のCPUにあるいろいろなgotchaを捨象している; キャッシュやパイプラインの振る舞いとか、アドレッシングモードによる 命令長や実行時間の違いとか。

こう考えると、抽象化には2種類あることになる。現実の部品と、そのモデル。 そして、部品を組み立てて、より大きな部品とすること (論理回路→組み合わせ/順序回路→ノイマンアーキテクチャ)。 モデル化にも、必要に応じていくつかの段階があろう (論理回路を理想的な ブール代数演算とみなすか、遅延やファンイン/ファンアウトまで考慮するか、等) 抽象化の網はこのようにふたつの方向にひろがっていることになる。

さて、現場で問題になる「抽象化の破れ」は主に、 モデルと現実の部品との間の差異に起因するように思える。 出力が1に確定しているはずなのが間に合わなくて時々0に見えたりする、とか、 「nextNode」を指すリンクにはノードクラスへのポインタか終端を表す値が 入っているはずなのにごみデータが混入していた、とか、 がんがん再帰してたらスタックが伸びすぎてヒープを上書きしてしまった、とか。

この時、トラブルシューティングをするには、まず「小さなモデル-大きなモデル」 という抽象化の階層を降りてゆき、破れが生じているレベルで「モデル-現実」 という抽象化の壁を越える必要があるわけだ。

「論理回路とその下の間に非常に強固な壁がある」というのは、そこでは 「モデル-現実」の壁が非常に厚いので、現場でそこの破れが問題になることが ほとんどないってことだ。より大きなモデルへと見て行った時に、 破れが起きやすい最初の場所が、CPUのISAなのだろう。だからトラブルシューターは 最悪、モデルの階層をCPUまで降りていって、現実との齟齬を調べることになる。

次はなんだろう。メモリとCPUからなるノイマンアーキテクチャのモデルかな。 スタックオーバーフローなんて話は、現実的には容量制限があるメモリと、 抽象的なスタックマシンというモデルとの間の齟齬と言えるかな。 あるいはバッファオーバランとか。言語モデルで見えている データは独立しているはずなのに、現実にはメモリ上に一直線に並んでいて 干渉することがある、とか。

あるいは、整数の演算というのは、モデルの階層は数学で作られている。 加減乗算については閉じているけど除算については云々、とかそういう話。 その世界では 1 + 1 も 10^100 + 1 も似たようなものだけれど、現実とのマッピング の段階でオーバフローを起こしたり多倍長演算が入って一方が極端に遅くなったりと、 齟齬が発生する。

まあ、この論自体もひとつのモデルであって、つっこみどころはたくさんあるけれど (「2種類の抽象化」って常に綺麗にわけられるのか、とか)、ここから何か無理矢理 考察を引き出すとすれば、

  • 「理解する」というのは「小モデル - 大モデル」の階層を降りてゆくことになる。 これはある意味きりが無い。一直線ではなく分岐があって、ひとつの流れにおいては 一番基礎となるモデルがあるかもしれないけれど、別の流れを降りて行くとどんどん 先が続いている、という具合。
  • 現場でトラブル解決には、「モデル - 現実」対応が破れている場所を 探す必要がある。こちらについてはある意味「きりが有る」。 現在の計算機を相手にしている限り、量子力学まで降りる必要は無い。
  • トラブルが「モデル - 現実」の齟齬だとすれば、 「小モデル - 大モデル」の構造を知らずに現実の部品ばかりを相手にしていても 役に立たない。CPUが大事だからって80386のアセンブラに飛びついても、 「CPUのモデル」からアプリケーション層に至るまでのモデル階層の理解が無ければ、 現実のトラブル解決が出来るようにはならないだろう。

うーん、結局「どっちも重要」っていうありきたりの話にしかならないなあ。

Tag: Programming