ぶていのログでぶログ

思い出したが吉日

rfコマンドv1.17.0をリリースした

前回の更新はこちら。 rfコマンドのv1.17.0をリリースした。リリースしたのは1月の末なのだが、私が新型コロナウィルスに罹患してダウンしていたのでブログの更新が遅れた…。健康第一

🏕 Features

高速化 5.2秒 -> 1.36秒

v1.15.0に引き続き更に高速化した。 v1.15.0のときは1MB、104334行のファイルのgrepに5.2秒かかっていたが、v1.17.0では1.36秒になった。73%ほど速くなった

$ rf --version
rf 1.17.0 (mruby 3.2.0 f41593f)

$ time rf -q -g hello /usr/share/dict/american-english

________________________________________________________
Executed in    1.36 secs      fish           external
   usr time  831.87 millis  194.00 micros  831.67 millis
   sys time  402.25 millis  122.00 micros  402.13 millis

遅くなっていた原因は、rfでは入力データの読み込みにはFile(IO)クラスではなくBufferdIOという独自のクラスを使っていた。 このクラスは、バッファをもたせたFile(IO)クラスで、入力データから先にバッファのサイズ分だけ読み出しておき、逐次そのバッファからデータを読み出すという処理にしていた。 このような処理にしている理由は、バイナリファイルの判定を行うためなのと、微々たるものだろうがIOの最適化である。

BufferdIOからデータを読み出す際に、String#slice!を呼び出しているのだがこれがボトルネックになっていた。 このボトルネックはperf record/perf reportコマンドを使って見つけ出した。 以下は実際にperf record/perf reportコマンドでrf v1.15.0の処理を分析した画像である。

str_convert_rangeという関数にほとんどの処理時間がさかれているのがわかる。 このstr_convert_ragetはString#slice!から呼び出されているのでこれが被疑だということがわかった。

解決策としてシンプルにBufferdIOをやめた。 複雑なことはせず改行コードまでを読み出し、そしてデータにバイナリが含まれていないかをチェックするという処理にした。 これにより、若干grepや今までの挙動と変わってしまうのだが些細な問題であろう。

ちなみに余談だが、rfにおいてこのFile(IO)を抽象化したクラスの作り直しは3度目になる。 もしかしたら、やっぱりバッファを持たせたほうがよい!と判断して作り直すかもしれない。 そうなったら次はC言語で書くことになりそうかな。

Enumerator#grep_vを呼び出せるようにした

元々mrubyにはないメソッドなので機能追加のPRをした。 https://github.com/mruby/mruby/pull/6148

特に問題なくマージしてもらったので、この変更を反映したmrubyを使うように変更した。

作成できるArrayのサイズの上限をなくした(MRB_ARY_LENGTH_MAX=0)

mrubyでは作成できるとArrayのサイズ上限が存在する。 これはおそらく組み込み環境のようにメモリサイズが限られた環境を想定した措置だと思う。 しかしながら、rfにおいてはそういったことを気にする必要がなく、必要があれば必要な分だけArrayを作りたいので上限をなくした。