涼風コンピュータblog

涼風 ・Rubyist, RubyやRuby on Railsに詳しくなっていきたいです

mruby-jsonとmruby-msgpackの速度比較してみた!

はじめに

JSONとMessagePackでは、シリアライズとデシリアライズにおいて、MessagePackがバイナリフォーマットなので、数倍速いです。今回はmruby版でシリアライズとデシリアライズの速度を比べてみようと思います。 ちなみにシリアライズはデータをファイルに保存したり、ネットワークで送受信できるようにすることです。反対に、デシリアライズとはシリアライズしたファイルやネットワーク上のデータをプログラムで扱えるように復元することです。

ビルド

1. build_config.rbにmruby-jsonとmruby-msgpackのconfig.gem行を追加:

MRuby::Build.new do |conf|

  # ...(省略)...
  conf.gem :git => 'https://github.com/mattn/mruby-json'
  conf.gem :git => 'https://github.com/suzukaze/mruby-msgpack.git'
end

2.コマンドプロプトからビルド:

ruby ./minirake

ベンチマークプログラム

自分はmacを使用しているので、計測したいmrubyファイルをmrubyで実行して、timeコマンドで計測すればよいです。 time mruby mruby-benchmark.rb

ですが、いくつも計測するのに汎用的なベンチマークプログラムはないかと探しましたら、rubyの標準にBenchmarkモジュールがありました。 よさそうと思ったのですが、すべて理解するのは大変だったので、少し参考にしてmruby用に自作しました。

SimpleBenchmarkクラス

class SimpleBenchmark

  def initialize(width = 0)
    @width = width
  end

  def measure(label)
    start = Time.now
    yield if block_given?
    passed = Time.now - start

    puts "#{make_fixed_label(label)}passed time #{passed} sec"
  end

  def make_fixed_label(label)
   if @width - label.length > 0
      label + ' ' * (@width - label.length)
    else
      label
    end
  end

end

次のハッシュをシリアライズとデシリアライズをJSONとmsgpackで10000回繰り返します。

{'name' => 'suzukaze', 'level' => 15})

計測プログラムは次のようになります:

benchmark = SimpleBenchmark.new("MessagePack:".length)

n = 10000

#JSONでシリアライズとデシリアライズする時間を計測する
benchmark.measure("JSON:") do
  n.times{ JSON.parse(JSON.generate({'name' => 'suzukaze', 'level' => 15})) }
end
#MessagePackでシリアライズとデシリアライズする時間を計測する
benchmark.measure("MessagePack:") do
 n.times{ MessagePack.unpack(MessagePack.pack({'name' => 'suzukaze', 'level' => 15})) }
end

計測環境

OS:Mac OS X 10.8.4

プロセッサ: 2.13GHz intel Core 2 Duo

計測

次のコマンドをコマンドプロンプトから実行します:

mruby mruby-msgpack-profile.rb

mruby-msgpack-profile.rbは最後に全部、掲載しています。

計測結果

JSON:       passed time 0.543839 sec
MessagePack:passed time 0.092397 sec

計測結果はMessagePackがJSONの約5.8倍の速さとなりました。

最後に

計測に使ったソースの全文を掲載します。

mruby-msgpack-profile.rb

class SimpleBenchmark

  def initialize(width = 0)
    @width = width
  end

  def measure(label)
    start = Time.now
    yield if block_given?
    passed = Time.now - start

    puts "#{make_fixed_label(label)}passed time #{passed} sec"
  end

  def make_fixed_label(label)
   if @width - label.length > 0
      label + ' ' * (@width - label.length)
    else
      label
    end
  end

end

benchmark = SimpleBenchmark.new("MessagePack:".length)

n = 10000

#JSONでシリアライズとデシリアライズする時間を計測する
benchmark.measure("JSON:") do
  n.times{ JSON.parse(JSON.generate({'name' => 'suzukaze', 'level' => 15})) }
end
#MessagePackでシリアライズとデシリアライズする時間を計測する
benchmark.measure("MessagePack:") do
  n.times{ MessagePack.unpack(MessagePack.pack({'name' => 'suzukaze', 'level' => 15})) }
end