MinCamlとクロージャ
MinCamlはクロージャまわりでハマることが多いらしいので、それっぽい記事のリンクを集めておく。
(あとで書く)
クロージャ変換
状態としてオリジナルのコードでは toplevel というグローバル変数を使っている。Haskellでは State モナドを使うことにする。
昨日、even-odd.mlで何も表示されなかったのは、末尾でないクロージャー呼び出しにblrではなくbrを使っていたのが原因だった。
足りない数学関数とか入出力関数とかを実装して、グローバル変数(min-rtはグローバル変数を外部変数として定義している。多分クロージャを作らないため)も用意してやったら、レイトレが動くようになった。
アセンブラに通ってからは、運のいいことにデバッグが素直に終わり(クロージャ回りが相当の闇でしたが…)
クロージャは凄いのですが、デバッグしたり、最適化する際に邪魔であります。出力されたアセンブリを読むのも、クロージャがあると、プログラムの流れがわかりにくくて萎えてしまうわけです。
全てのクロージャを完全に潰す事はできないのですが、幸いレイトレプログラムはクロージャを潰せるような書かれ方しかされていないので、遠慮無く潰すのが良いです。
潰し方は、単純に引数に渡すようにするだけ。簡単ですね。
まあぶっちゃけこれを実装しなくても上述のヒープ領域コンパイル時割り当てをすれば(レイトレプログラムだと)全てのクロージャは生まれる前に死ぬのですが。
クロージャ呼び出しを正しくコンパイルできないバグを修正した。
レイトレを動かしたい(主にクロージャー周りのメモリアクセスで落ちる)
・クロージャーむかつくよね
・ 全てのクロージャーを生まれる前に殺せ
今は逆にヒープが余っている(クロージャを撲滅させたため)
ライブラリとクロージャあたりのバグを修正した所、スケルトンな画像が出てきた
raytracer/min-rt.mlがクロージャレジスタまわりをいじったらシミュレータ上で動いた
また、min-rt.mlの初期化ルーチンの改変は許されているので、実質的にグローバル変数として確保されている配列をアセンブラに移して、MinCamlの上でクロージャが作られないようにしておいた(無論グローバル変数の検出をコンパイラで行うのが本当は正しい)。進捗発表直前に地下でコンパイラ係と二人で作業して何とか間に合わせた。
グローバル変数の導入
レイトレのプログラムは先頭で定義される配列を後ろの関数群がなんども読み書きするような構造になっているので、ほとんどの関数に自由変数があり関数呼び出しはクロージャを介したものになります。そこで初めて関数が定義されるまでに定義される変数たちをグローバル変数として扱うことでオーバーヘッドの大きいクロージャ呼び出しをなくすことができます(これはレイトレのソースの特性上そうなるということです)。具体的には配列やタプルのアドレスをコンパイル時に確定しておいて、それ以降でのアクセスではメモリの定数番地に対するアクセスとして処理するようにします。
クロージャ生成の時に、定数変数が関数の中で自由変数にならないようにした
ここまでに行った最適化は、ライブラリを書き換えてクロージャが作られないようにしたり、零レジスタの活用、定数テーブルやグローバル配列の読み込みをラベルから直接行うように、といった単純なのくらい。