[SikiLanguage] [式鬼言語航海日誌]
2008-08-23
2008/08/23_193846では、今日は『駆動』についてです。
「実行プロセスっつうた方が分かり易くないかい?」
何となく実行プロセスというよりも駆動と言った方がしっくり来るんですよね。何でか知りませんが。
さて、前回も軽く触れましたが、式鬼言語は何でもかんでもCellが絡んできます。特に、式鬼言語を動かすバーチャルマシン自体もCellでできているのは特筆すべきことじゃないかと思います。
「普通は別の構造にした方が効率良いからな」
しかし、将来的に式鬼言語からバーチャルマシンを弄ることを考えると、やっぱり同じ構造にしておいた方が便利そうなんですよね。最初はC++ネイティブでバーチャルマシンを構築することも考えましたが、『どうせ誰も使わないお遊び言語なんだからトコトン突き詰めてやってみたほうが面白くね?』ということで突っ走りました。
「そのために一年ぐらい開発が足踏みしたけどな」
あれ、そんなに長かったでしたっけ? まあ、その辺は私の知識不足の部分もありますので。嫌んなって放っといた時期もありますし。
…………ああ、才能と根気が欲しいなぁ。
「で、実行プロセスはどうしたんだい? C++は継続とかコルーチンとか無いからかなり面倒そうだけど」
そうですね。ここいらの実装に関してはC++は不自由ですね。気をつけないとスタックフレーム使い切っちゃいますし。基本的には式鬼言語側で駆動を管理する形にしています。
では、さっそく始めましょう。
まず『駆動(activation)』について。駆動とは、関数とか手続きとかの一連の処理を行うことですね。ぶっちゃけ“実行プロセス”とか“実行ステップ”とか言っても大外れではないと思います。ただ“実行”というとプログラム全体の大きな枠組みを意識する場合がありますが、今回は実行の最小単位となる命令を実行する手順が焦点になりますので、ここでは『駆動』としています。
「『式鬼言語におけるコマンド処理のメインループの中の手順』、つうことかい?」
まあ、そんな感じですね。
他の言語では何と言っているか判りませんが、式鬼言語ではこの駆動が最小の処理単位となります。さらに言うと、駆動一回で処理できないコマンドも多々存在します。
さっそくバーチャルマシンの構造を見ていきましょう。まずは最低限の本当に核の部分だけ。本当はもうちょっと複雑なんですけど、それは核の部分を説明してからにします。式鬼言語のバーチャルマシンでは、YarnとFiberという2種類のCellが中心になります。
- Yarn
- List : [Fiber0, Fiber1, Fiber2, ……, FiberN, CommandX]
- Fiber
- List : [CommandN, ……, Command2, Command1, Command0]
- Map : [<Stack>]
この2つがバーチャルマシンのキモですね。Yarnが現在実行中の駆動を管理して、Fiberが実行中のスコープを管理します。
Yarnの方は簡単ですね。実装言語の駆動をループの形で独占して、ループ一回毎にListのトップにあるCellを実行します。
- 駆動をループとして独占する
- 一回の駆動毎に
- ListのトップにあるCellを取り出す
- Cellに駆動を渡して実行する
- Listが空になったら駆動を手放す(終了)
通常、Fiberのように別のCellを実行するCellは、処理が終了するまで自分自身をYarnのListに積んで実行したCellの終了を待ちます。
「ふうん、なんか関数スタックみたいだね」
そうですね。関数スタックみたいなものです。スタックを活用してスコープの入れ子構造も表現しています。
Fiberの方はもうちょっと面倒ですね。Fiberは呼ばれるごとにListのトップにあるCellを実行します。また、Commandに引数を渡して実行結果を受け入れるために、<Stack>と呼ばれる特殊Cellとリンクしています。基本はスタックマシンなので、<Stack>は一つあれば十分です。
- 別のCellから駆動を渡される
- 一回の駆動毎に
- ListのトップにあるCellを取り出す
- Cellに駆動を渡して実行する
- 駆動を返す
「あれ? さっきのYarnと同じような動きをするね」
はい、そうですね。基本的には同じです。実際には実装言語のC++側の駆動の問題もありますので、FiberのListからYarnのListに積み替えて(実装言語側の関数スタックを抜いてから)Cellを実行していますが……
「Schemeみたいに継続使えると簡単なのにな」
C++の関数スタックはケアしないで無視しようかなぁ。
閑話休題。
FiberとYarnの大きな違いは駆動の占有期間ですね。YarnはList中の全てのCellを処理するまで駆動を手放しませんが、Fiberは1つのCellを処理したら駆動を手放します。
「<Stack>の部分も違うんじゃない?」
ここはちょっと検討中です。Yarnは確かに引数渡し/戻り値受け取りの機能を持たしていませんが、実際にはプログラム全体の引数を渡すのとかプログラムの実行結果を保存するとかに使えるかもしれないので。
「ふうん、YarnのListをコールスタック的に扱って、FiberのListに命令列を保存してアルゴリズムを管理してるのか。Fiber自体には関数フレームの機能もあるから、引数とか戻り値とかはここで管理する、と」
ええ、このようにYarnとFiberの役割を分けることで、YarnとFiberの相似性を上げています。全てのものが斉しい、美しい対称性の世界にようこそ…………
「そこまでは言えないだろ」
まあ、そうですけど……ずいぶん頭悩ませたところなんですから、これぐらいは言わせて下さいよ。
ついでなので、駆動に関係のあるCellを全部挙げちゃいましょう。前に説明した内容から全然別物になってますけど……
- Monomer:
- アトミックな命令を表わすCell
- Polymer:
- 複数の命令を組み合せたCell
- 実行したYarnのトップにあるFiberのListに、実行する命令を流し込む
- Filament:
- 複数の命令を組み合せたCell
- 命令を一つ一つ実行する
- Fiberと異なり、スタックフレームにならない
- FiberのListのトップに自分自身を積む
- Fiber:
- 複数の命令を組み合せたCell
- 命令を一つ一つ実行する
- Yarnのトップに自分自身を積む
- 引数と戻り値を管理する
- Thread:
- 複数の命令を組み合せたCell
- 新しいネイティブスレッドを作成して独占し、命令を全て実行する
- Yarn:
- 複数の命令を組み合せたCell
- 実装言語のプロセスを独占し、命令を全て実行する
といった感じですね。
「呼び名は繊維関係か。だからProcessじゃなくてYarnなのかな?」
そうです。Thread, Fiberの名前から発想を頂きました。まあ、悪くないでしょ?
「どうだろ?混乱しそうな気もするけど」
制作・著作: 野分(nowake) at fiercewinds.net (Creative Commons 表示-継承 2.1 日本)