REXML とlibxml-ruby の解析速度を比較して Gruff で描画してみた

Rubyの文字列の連結速度を測定してGruffで表示してみた - すえひろがりっっっっ! の使い回し。
REXML は pure ruby なので個人的に使う分には気軽に使えていいのですがびっくりするぐらい遅いので、システムには組み込めません。というのをグラフで表示してみた。

環境

ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]
libxml-ruby (1.1.4)
gruff (0.3.6)

実験コード

<root>
  <child/>
  <child/>
  <child/>
  ...
</root>

みたいな単純な構造のXMLを REXML と libxml-ruby で解析します。
子要素の数を徐々に増やしてそれぞれにかかった時間をプロットしていきます。

require 'rubygems'
require 'gruff'
require 'benchmark'
require 'xml/libxml'
require 'rexml/document'
# 最大子要素数
MAX = 10000
# 刻み
STEP = 100

g = Gruff::Line.new

# <root><child/><child>...</root> のような単純な XML を作成
# 引数 num だけ child 要素を作る
def build_xml(num)
  xml = '<root>'
  num.times do 
    xml << '<child/>'
  end
  xml << '</root>'
end


# XMLの大きさを順次変更し、yield で渡して解析時間を計測する
# 返り値は計測した時間の配列
def bench
  benchmarks = []
  0.step(MAX, STEP) do |i|
    xml = build_xml(i)
    benchmarks << Benchmark.measure do
      yield xml
    end 
  end
  benchmarks.collect{|b| b.real}
end

g.theme_37signals
# REXML で解析して plot 
g.data("rexml", 
  bench do |xml|
    REXML::Document.new(xml).elements.each("root/child") {|_|}
  end
)
# libxml-ruby で解析してplot
g.data("libxml-ruby", 
  bench do |xml| 
    XML::Document.string(xml).root.find('/root/child').each {|_|}
  end
)

0.step(MAX, MAX / 4) do |i|
  g.labels.merge!({i/STEP => i.to_s})
end
g.x_axis_label = 'Times'
g.y_axis_label = 'seconds'
g.write('result.png')

結果


まとめ

  • REXML 重すぎ。10000行程度で秒単位になるとは思わなかった。本番システムでは使えない。
  • Gruff 楽しすぎ。