前回のあらすじ http://ksss9.hatenablog.com/entry/2013/10/19/131215
以前書いたAsciiPackというgemをrubyのコードからc拡張へと書き換えてみました。
AsciiPack https://github.com/ksss/AsciiPack
差分 https://github.com/ksss/AsciiPack/commit/d8a53dddd4141c46af8a936e0c6787bfc8b1d37f
これが結果として驚くほど早くなりました。(2倍〜40倍くらい)
いくつかの特徴的なオブジェクトで各メソッドを10000回呼び出して計測しました。(単位は/ms)
以前のはこちら https://gist.github.com/ksss/7096868
速度アップのポイントは「如何にrubyを使わないか」にあると思います。
例えばrubyのcase文では100個when
があれば毎回100回===
メソッドを呼び出すことになります。
ここの部分をc化するだけでも100回のrubyのメソッド呼び出し分のオーバーヘッドがなくなるのでかなりの効果が期待できます。
また単純な四則演算や1文字取り出すだけでもrubyならメソッド呼び出しのコストが掛かりますが、cで書けばそのコストは削減できます。
逆にc拡張化してもrb_funcall
のようなrubyのメソッド呼び出しをしていては全く速度は変わりません。
文字はポインタを進めながら読み、メモリもcレベルで管理し、ruby<->cのインターフェースになる部分でrubyの内部フレームワークを使うのが良いと思いますしrubyの内部コードもそうなっています。
今回ベンチマークを取ってみて、小さな数字などではMessagePackを上回っている部分もあれば、長い文字列ではMessagePackが圧倒的な速さを出しているということがわかりました。
これはMessagePackが謳っているようにzero copyの効果だと考えられます。
AsciiPackではどんなに長い文字列でも現状では別のバッファ領域に文字をコピーしてオブジェクトを作成していて無駄だなーと思っているのでここもMessagePackを見習ってzero copyを実装できたらなと思います。
MessagePackより早くなってしまった部分については
- メモリ管理が不十分(なんせ
rb_gc_mark
を1度も書いていない……) - AsciiPackの機能が単純すぎるため
- 誤差の範囲
なんかが考えられるのかなと思います。
とりあえずオライリーの「Head first C」を買ったので早く届くといいな……。