ぶていのログでぶログ

思い出したが吉日

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

前回の更新はこちら。 昨日の記事で書いたmruby-yysjonを早速組み込んだのと、ちょっとした不具合の修正をした。

前回からの記事の変更点

mruby-yyjsonの利用を始めた

折角つくったのでmruby-yyjsonを組み込んだ。 これにより以下の点が改良された。

  • キーが重複しているJSONのObjectを読み込んでもエラーにならなくなった
    • 例えば '{"foo":"bar", "foo":"baz"} みたいなJSONデータ
  • 不正なJSONを読み込んだときにエラーがわかりやすくなった
    • Error: failed to parse JSON: unexpected end of data position: 2 みたいなエラーがでるようになった
  • JSONの読み込みの高速化

JSONの読み込みがどのくらい高速化されたかベンチマークをとってみた。 入力データは昨日の記事と同じでcanada.jsonとfgo.json。 使用したコマンドはそれぞれ以下の通り。

  • mruby-yyjsonを組み込んだrfコマンド(v1.20.0)
  • mruby-jsonを組み込んだrfコマンド(v1.19.0)
  • 参考用にjqコマンドのv1.7.0*1
  • 参考用にCRubyのJSON.#parse

結果は以下の通り。

# canada.json
$ hyperfine --input canada.json --warmup 3 './build/bin/rf -j -qs _' 'rf -j -qs _' 'jq -sMrc .' 'ruby -rjson -e "JSON.parse(STDIN.read)"'
Benchmark 1: ./build/bin/rf -j -qs _
  Time (mean ± σ):      12.2 ms ±   0.4 ms    [User: 6.7 ms, System: 2.0 ms]
  Range (min … max):    11.3 ms …  13.4 ms    237 runs

Benchmark 2: rf -j -qs _
  Time (mean ± σ):      58.9 ms ±   1.1 ms    [User: 47.7 ms, System: 6.5 ms]
  Range (min … max):    57.4 ms …  64.6 ms    51 runs

Benchmark 3: jq -sMrc .
  Time (mean ± σ):      65.3 ms ±   1.0 ms    [User: 52.6 ms, System: 8.4 ms]
  Range (min … max):    63.7 ms …  68.4 ms    46 runs

Benchmark 4: ruby -rjson -e "JSON.parse(STDIN.read)"
  Time (mean ± σ):      94.0 ms ±   2.2 ms    [User: 80.7 ms, System: 9.3 ms]
  Range (min … max):    91.0 ms … 100.8 ms    32 runs

Summary
  ./build/bin/rf -j -qs _ ran
    4.81 ± 0.18 times faster than rf -j -qs _
    5.33 ± 0.19 times faster than jq -sMrc .
    7.68 ± 0.30 times faster than ruby -rjson -e "JSON.parse(STDIN.read)"

# fgo.json
$ hyperfine --input fgo.json --warmup 3 './build/bin/rf -j -qs _' 'rf -j -qs _' 'jq -sMrc .' 'ruby -rjson -e "JSON.parse(STDIN.read)"'
Benchmark 1: ./build/bin/rf -j -qs _
  Time (mean ± σ):     691.7 ms ±   9.3 ms    [User: 555.4 ms, System: 102.7 ms]
  Range (min … max):   680.8 ms … 712.4 ms    10 runs

Benchmark 2: rf -j -qs _
  Time (mean ± σ):      1.477 s ±  0.008 s    [User: 1.220 s, System: 0.200 s]
  Range (min … max):    1.468 s …  1.490 s    10 runs

Benchmark 3: jq -sMrc .
  Time (mean ± σ):      2.007 s ±  0.011 s    [User: 1.786 s, System: 0.137 s]
  Range (min … max):    1.992 s …  2.023 s    10 runs

Benchmark 4: ruby -rjson -e "JSON.parse(STDIN.read)"
  Time (mean ± σ):     815.1 ms ±   5.8 ms    [User: 710.9 ms, System: 64.8 ms]
  Range (min … max):   803.9 ms … 825.7 ms    10 runs

Summary
  ./build/bin/rf -j -qs _ ran
    1.18 ± 0.02 times faster than ruby -rjson -e "JSON.parse(STDIN.read)"
    2.14 ± 0.03 times faster than rf -j -qs _
    2.90 ± 0.04 times faster than jq -sMrc .

どの結果でもmruby-yyjsonを組み込んだrfが一番はやかった!やったー! jqより速いのはrfの優位性を強調することができるかな? CRubyはコマンド起動時のオーバーヘッドがあるのか小さい(時間のかからない)ファイルだと遅い結果になってしまったが、大きいファイルになっても高速に動作しているので安定性が高いと思う。

mrubyのソースコードをrfのサブモジュールとして取り込むようにした

いままではmruby-buildのDockerイメージにmrubyのソースコードを含めていたのだが、mrubyをアップデートしたいときにmruby-buildのイメージをアップデートしないといけないという手間があった。 そのためmrubyのアップデートに追随するのが非常に手間だったのだが、mruby-buildにmrubyのソースコードを含めないようにしたのでrfリポジトリのサブモジュールとして取り込むようにしたという感じ。 またmruby-buildのイメージに同梱させるかもしれないけど、一旦こんな感じ。

宣伝: rfコマンドハンドブック公開中

zenn.dev

*1:イテレーションや無駄な出力をしないようにして最適化したつもり