Let's start Scheme

2012-03-21

Weak hashtable

マクロ周りの修正と、デバッグ情報(主にファイル周りの情報)を何とかしようとしたらメモリがあっという間に飛ぶようになったので、ちょっとまじめに実装しようとしてみた。

問題になったのは、VM側にソースの情報を持たせているのだが、こいつが普通のハッシュテーブルなので要素は増えるけど減らないというのがある。たとえばユニットテストなんか走らせると最低でも500MBぐらい必要だった。

元もと使ってはいなかったがWeakハッシュテーブル自体はコードにあって(Gaucheからのパクリ)、単にこれを使えばいいかなぁと思っていたのだがそう単純な話でもなかった。

何が問題だったか。
GaucheのWeakハッシュテーブルはキー、値、もしくは両方という感じでどの要素がWeakポインタなのか指定できる。しかし、そのままの状態でうまく動いたのは値を指定した場合のみで、キーまたは両方を指定すると値が取り出せないか最悪クラッシュする。(最新版のソースは知らない+Gauche上で使ったことないのでその辺は考慮されているのかもしれない)

どうしてそれが起きたのか。
Weakハッシュテーブルに要素を入れるとき、GCが辿らないように要素を一段ラップするようになっている。問題はここで、値だけなら問題ないが、キーをラップしたときラップされたポインタは闇に消えていた。なので、値を取り出そうとすると必ず失敗する。(だって、キー自体とラッパーは違うんだもん)

ではどうしたか。
非常に醜い方法でとりあえず回避。ハッシュテーブル自体にラップされたキーの情報を持たせるようにした。値を入れる際にキーの情報も一緒に保存しておく。っで、取り出す際には保存先から生のキーを元にラップされたキーを取り出す。ただ、これ自体がGCから辿れると結局回収されないので、これ自体もWeakベクタで実装。ただ、キーの情報が増えるとベクタが爆発的に増えるのでパフォーマンスに影響がでる。(というか出てる)。

最終的にはメモリ使用量が最大で200MBくらいに落ち着いたので、まぁよしとしている(これ以外にもいろいろやっているが)。キャッシュされてしまえばソース情報は消えるわけで、コンパイルもスキップされるから速度的には問題なくなると思うし。

追記:
さすがにあまりにコンパイルが遅いので上記の方法はやめた。普通にラッパを判別可能にして回避。ハッシュ関数と比較関数側でなんとかするようにした。なにしろ、ビルド時に走るソース変換のプロセスが待てないレベルで遅かったので。

No comments:

Post a Comment