Let's start Scheme

2014-11-08

デザインミスとI/Oパフォーマンス

最近サポート業務が多く、ログファイルを眺めて原因を探るという作業が非常に多い。毎回lessで開いて特定の情報のみを目grepするのは馬鹿らしいが、覚えにくいシェルコマンドを複数回叩くとかもやりたくないなぁと思いSchemeでログ解析するスクリプトを書いた。ここまでが導入。

っで、今更になって文字列の内部表現をUTF-8にしておけばよかったなぁということを後悔している。問題になっているのはメモリ使用量で、上記のログは一行10万文字とか普通にあってこういうのを複数回読み込むとGCが警告メッセージを大量に吐き出してくる。ちなみにBoehm GCは内部的に巨大なメモリ割付の閾値を持っていて、LARGE_CONFIGだと64(係数) * 4098(ページサイズ)となっている。ログファイルのテキストは全部ASCIIなのでUTF-8であれば10万バイト(100KB)で済むのにUCS32にするから400KB持って行かれる。このサイズが数回でてくる程度であれば問題ないんだけど、ログファイルは70MBくらいあって、20~30行に一回くらいの頻度で巨大な一行がでてくる。そうするとCygwinの初期ヒープサイズだとメモリが足りなくて死ぬ(ので、初期ヒープを2GBに拡張している)。これだけならいいんだけど、ログファイルにはバイナリも吐き出されてて、こいつを文字にするとバイナリの情報が落ちる(これはUTF-8に変換する際にも同様のことが起きる可能性があるのでどっこいかな?)。

今更こいつらを変更するとなるとかなり大変だし、一文字=一要素を想定して最適化してある部分もあるのでパフォーマンスの劣化も気になる。作った当初はこんな巨大な文字列扱う予定なかったからアクセス速度の方を優先してしまったが、ちと失敗だったかもしれない。

上記のログファイルの読み取りに関連するのだが、get-lineが遅い。例えばGaucheと比べるとおよそ5倍から遅い。理由は実にはっきりしていて、文字ポートとバイナリポートが分かれているのと、一文字読むのにコーデックによる変換が入ること。これはR6RSが要求していることなのでどうしようもないのではあるが、それでもなぁというレベルで遅い。ちらっとGaucheのread-lineの実装をみたのだが、Gaucheでは'\n'が出るまで1バイトずつ読むという方針で、あぁそりゃ速いわという感じであった。ちなみに、この方針は使えないので(バイトベクタを読むならいいけど、文字では・・・)どうしようもない。

こうなってくると文字ポートの使用をやめてバイナリを直接扱うようにしていかないととなる。そうなると問題は正規表現で、現状では文字列しか受け付けない(バイトベクタを扱うユーティリティはかなりそろえた)。ASCII限定にしてバイトベクタ対応させると多少嬉しいだろうか?(文字にするとオーバヘッドが大きすぎる気がする)

さて、どうしたものかね・・・

No comments:

Post a Comment