前回の更新はこちら。 昨日の記事で書いたmruby-yysjonを早速組み込んだのと、ちょっとした不具合の修正をした。
前回からの記事の変更点
- Use mruby-yyjson instead of mruby-json by @buty4649 in https://github.com/buty4649/rf/pull/205
- fix: bug: file names included in files when using -i option with multiple files by @buty4649 in https://github.com/buty4649/rf/pull/203
- Add mruby submodule to the project by @buty4649 in https://github.com/buty4649/rf/pull/197
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コマンドハンドブック公開中
*1:イテレーションや無駄な出力をしないようにして最適化したつもり