2016-08-11

不要レジスタの除去とSchemeコード

VMにレジスタを追加するとスレッドセーフなパラメタと同様な感じで使えるということがあり、SagittariusではVMにいくつかマクロ展開器用のレジスタを持たせていた。あまりいい手ではないし、無駄にVMのサイズを増やすことになるのでいつかは何とかせねばと思いつつもかなりの期間放置していた。っが、最近いい方法を思いついたのでえいや!と除去。VMのサイズが5ワード減って752バイト(64ビット環境)になった。焼け石に水もいいところである。

【やったこと】
基本的には非常に簡単で、VMのレジスタをScheme側でパラメータ化してそれらを使っていたCの実装を全てScheme側に移動させただけ。Scheme側といってもプレビルドされているものなので、本体サイズが小さくなるということは残念ながらない。単にVMのサイズが多少減るのと、自己満足度が多少上がっただけである。

言うは易し行うは、キャッシュのせいで、多少難かった。除去したVMレジスタはマクロ展開器ようにしぶしぶ追加したもの。こいつのせいでマクロのキャッシュが無駄に複雑になっていた(今でも不必要に複雑だが)。SchemeのパラメータをCから呼び出すということは可能な限り避けたいと思ったので、マクロ展開器に関するコードをごそっとScheme側に移動させる必要があった。そうすると、Cで作られた展開器と密接につながっていたキャッシュの読み出しと書き出しが壊れる。キャッシュ機構自体が無駄に複雑なので面倒なデバッグに突入。後は気合でって感じだった。

これによるパフォーマンスの低下があるかなぁと思ったけど、顕著に表れるようなものはなかったのでよし。

【Schemeコードの混在】
VMのレジスタにはリーダーマクロやR7RSのincludeのためにあるものもあった(取っ払ってやった、更に2ワード減ったぜ)。こいつらはコンパイラが依存していたり、load手続きが使用していたりするので、単純にプリコンパイルされたSchemeに放り込むことができなかった。気合でパラメータ関連をCで書いて何とかするという手もなくはなかったんだけど、年を取ると楽な方に逃げたくなる。ということで、スタブファイル内にSchemeコードが書けるようにしてみた。

Schemeコードのプリコンパイル自体はすでにあるのだから、ちょっと手を入れればいけるだろうと思って手を入れてみたら動いたという感じ。残念ながらCで書かれた手続きからSchemeの手続きを呼ぶことはできないし、SchemeからCの関数を直接呼び出すこともできない。それでも、混在可能というのは非常に楽である。依存関係があるので純粋にSchemeで書くより多少気は使うものの(ちなみにマクロは使えない)、Cでごにょごにょやるより遥かに楽である。これを使って(sagittarius)ライブラリ内に簡易パラメータを作り、load周りのVMレジスタをそいつで実装。それに伴って関連するCのコードをごそっと消してやった。すっきり。

流石にこれはパフォーマンスに影響がでて、例えば(rfc http)のように大量に他のライブラリ(数えたら131個あった)に依存するものをキャッシュなしで読み込むと大体10~15%程度パフォーマンスが落ちた。Cでは手続き内でやっていた処理がScheme側に移動したことで手続き呼び出しに変わったこと等によるオーバーヘッドだろうなぁとは思っているものの、まぁしょうがないか。キャッシュになってさえいればほぼ変わらないので(キャッシュはCなので当たり前だが)、初回起動のみが遅いと思えばそこまで気にすることでもないかなぁ。そもそもコンパイルとマクロ展開が遅いでそっちを先に何とかしないとという話である。(text sql)のコンパイルとか環境によっては10秒かかるし…2000行以上にわたるマクロを10秒で展開すると思えばそこまで悪くないともいえるのか?っがコンパイル待ってる間にコーヒー飲み終わる勢いなのは流石になぁ…

この調子でVMの不要もしくはあってほしくないレジスタを削除していきたいところである。

2 comments:

Anonymous said...

sagittarius-0.7.7をUbuntu-16.04-x86_64にインストールしようとしましたが、
make testで、こんなことになってしまいます。
----------
%%%% Starting test RFC HTTP tests
# of expected passes 22

Unhandled exception
Condition components:
1. &socket-closed #
2. &who socket-recv
3. &message socket is closed
4. &irritants #
5. &stack-trace

stack trace:
[1] socket-recv
[2] call-with-bytevector-output-port
[3] read-record
[4] %tls-socket-recv
[5] dynamic-wind
[6] tls-socket-recv
[7] read!
[8] get-line
[9] loop
src: (get-line in/out)
[10] dynamic-wind
[11] #f
src: ((call/cc (lambda (guard-k) (lambda () (with-excep
%%%% Starting test HPACK
# of expected passes 27
%%%% Starting test HTTP2
# of expected passes 179

%%%% Starting test HTTPS tests
FAIL http-get, custom receiver
expected value: #f
actual value: #>
#<&who socket-send>
#<&message Connection reset by peer>
#
>
# of expected passes 7
# of unexpected failures 1
...
%%%% Starting test (run-zlib-test)
# of expected passes 10

CMakeFiles/test.dir/build.make:57: ターゲット 'CMakeFiles/test' のレシピで失敗しました
make[3]: *** [CMakeFiles/test] エラー 1
CMakeFiles/Makefile2:99: ターゲット 'CMakeFiles/test.dir/all' のレシピで失敗しました
make[2]: *** [CMakeFiles/test.dir/all] エラー 2
CMakeFiles/Makefile2:106: ターゲット 'CMakeFiles/test.dir/rule' のレシピで失敗しました
make[1]: *** [CMakeFiles/test.dir/rule] エラー 2
Makefile:175: ターゲット 'test' のレシピで失敗しました
make: *** [test] エラー 2
----------
sagittarius-0.7.5以降で、同じような状態だった気がします。

kei said...

CI上でも同様の現象を確認しているのですが、手元で再現しないので原因がつかめずにいるやつですね(なのでCI環境固有のものと思ってました)。とりあえず、Issue に挙げておきました。
https://bitbucket.org/ktakashi/sagittarius-scheme/issues/192/https-tests-occasionally-fails-due-to
テストがこけているだけなので、結果を無視して make install してもらえばインストールは可能です。

Post a Comment