@nqounetです.

MojoliciousでJSONを使う時は,renderメソッドにjsonを指定してやると変数をそのままJSONにして渡せるので簡単です.

1
2
my $args = { a => [1,2], b => 'c' };
$self->render(json => $args);

しかし,この時に使用されるMojo::JSONはPurePerlなので,できればXSのモジュール(JSON::XS)を使って高速化したいですよね.

計測


その前に,Mojo::JSONとJSON::XSではどのくらい速度が違うのかを調べてみます.

ついでなので,便利に使えるJSONも一緒に測ってみます.

ソースコードは最後に載せていますので適当にお使いください.

1
2
3
4
5
6
7
8
# Benchmark: running json, mojo, xs for at least 3 CPU seconds...
# json: 3.03104 wallclock secs ( 3.02 usr + 0.00 sys = 3.02 CPU) @ 173870.86/s (n=525090)
# mojo: 3.19469 wallclock secs ( 3.17 usr + 0.00 sys = 3.17 CPU) @ 14308.83/s (n=45359)
# xs: 3.16628 wallclock secs ( 3.15 usr + 0.00 sys = 3.15 CPU) @ 195861.27/s (n=616963)
# Rate mojo json xs
# mojo 14309/s -- -92% -93%
# json 173871/s 1115% -- -11%
# xs 195861/s 1269% 13% --

mojoはMojo::JSON,xsはJSON::XS,jsonは,JSONです.

JSON::XSの方がMojo::JSONよりも10倍以上速いですね.

JSONモジュールは,JSON::XSがあるとこれを使うのですが,若干差がありますね.

インスタンスを作るときなどに多少のオーバーヘッドがあるのかもしれません.

ちなみに,インスタンスを再利用するとこんな感じの結果になりました.

1
2
3
4
5
6
7
8
# Benchmark: running json, mojo, xs for at least 3 CPU seconds...
# json: 3.18115 wallclock secs ( 3.16 usr + 0.00 sys = 3.16 CPU) @ 285812.66/s (n=903168)
# mojo: 3.26202 wallclock secs ( 3.22 usr + 0.00 sys = 3.22 CPU) @ 14538.20/s (n=46813)
# xs: 3.19833 wallclock secs ( 3.18 usr + 0.00 sys = 3.18 CPU) @ 309832.39/s (n=985267)
# Rate mojo json xs
# mojo 14538/s -- -95% -95%
# json 285813/s 1866% -- -8%
# xs 309832/s 2031% 8% --

…さらに差がつきましたね.

ここまで速いのであれば,使える環境なら使いたいですね.

MojoliciousでJSON::XSを使う


MojoliciousでJSON::XSを使う場合は,Mojo::JSON::XSが便利です.


Mojo::JSONとメソッドの互換性があり,JSON::XSでは使えないtrueやfalseも使えます.

最近は更新がないのでどこまで互換性があるか心配ではありますが,少し使ってみた感じでは問題ありませんでした.

プラグインを書いた


使い方としては,rendererのadd_handlerでjsonのハンドラを上書きする方法が安全ですが,render以外でもMojo::JSONを利用している部分があるので,大胆にMojo::JSON::newを(再)定義してやります.

使う時はいつもと同じようにpluginメソッドで呼び出すだけです.

1
2
3
4
5
# Mojolicious::Lite
plugin 'JSON_XS';

# Mojolicious
$app->plugin('JSON_XS');

正直なところ,JSON部分だけが速くなってもあまり効果はない気がします.

ただし,とてつもなく大きなJSONをやりとりする機会があるなら使えると思います.

ハンドラーを書き換える方法はこちらをどうぞ.

余談


こんなことを考えたのは,この記事がきっかけでした.


JSON::XSを使いたい気持ちはわかるので.

ベンチマークのソース


ということで,忘れかけていたベンチマークのソースです.

#!/usr/bin/env perl
use utf8;
use v5.12;
use App::Benchmark;
use Mojo::JSON;
use JSON::XS;
use JSON;
my $args = {
  hoge => 'hoge',
  geho => {geho => 'gehoge', hoge => 1, hum => [1, undef, 'hoge']},
  foo => ['bar', 'baz', 2],
};
benchmark_diag(
  -3,
  {
    mojo =>
      sub { my $json = Mojo::JSON->new; my $ret = $json->encode($args); },
    xs   => sub { my $json = JSON::XS->new; my $ret = $json->encode($args); },
    json => sub { my $json = JSON->new;     my $ret = $json->encode($args); },
  }
);