soutaro さんが作っている StrongJSON をより高速に実行する MightyJSON を作っています。
StrongJSON とは
StrongJSON とは、JSON の型チェックを行う便利Gemです。
# スキーマ定義 s = StrongJSON.new do let :item, object(id: number, name: string, count: number) let :customer, object(name: string, address: string, phone: string, email: optional(string)) let :order, object(customer: customer, items: array(item)) end json = JSON.parse(input, symbolize_names: true) # json の値が定義したスキーマに合致すれば、それが order 変数に代入される。 # 合致しなければ例外が投げられる order = s.order.coerce(json) # === が定義されているので、case-when に渡すことも可能 case json when s.item # json が item であればここが実行される when s.customer # json が customer であればここが実行される end
このような便利Gemなのですが、社内でsoutaroさんが「StrongJSONがボトルネックになっているかもしれない」ということを呟いているのを小耳に挟みました。 そのボトルネックは別の方法で解消されたようですが、面白そうなので高速化をしてみることにしました。
ベンチマーク
実際にどのくらい高速化出来たのかを示します。
縦軸は実行時間(秒)。 テストケースは https://github.com/pocke/mighty_json/tree/master/benchmarks に存在します。 また、各テストケースの実行回数はまちまちです。 例えば、Large-dataは20回しか実行していませんが(その代わり1回が大きい)、objectは2000000回実行しています。
Large-data は実用的なデータセットですが、この場合2.8倍程度高速になっています。 またどのケースでも MightyJSON の方が数倍速くなっているのがわかると思います。
なお、MightyJSON の master では更に高速化がなされていますが、ベンチマークをし直すのがめんどくさかったので少し古いベンチマークとなっています。
実装方法
MightyJSON では、高速化の為に「スキーマ定義時にRubyコードを生成する」という戦略を取っています。
これによりより最適なコードを実行できること、またメソッド呼び出しの回数を減らすことが可能です。
これは todesking/patm と同じ戦略です。
- https://github.com/todesking/patm
- http://www.todesking.com/blog/2014-05-26-patm-fast-pattern-matching-gem-for-ruby/