Ruby 2.4 で CGI.unescape が高速化されていた

久しぶりに ruby をいじる機会があったので、備忘的に。

URL encode された文字列を decode したいのだけど、Ruby 2.1 の URI.decode だと遅すぎる!

というのをなんとか解決できないかと調べていました。

ベンチマークコード

似たようなことをやってくれるメソッドが複数あったので、とりあえずベンチとってみる。

require 'benchmark'
require 'uri'
require 'cgi'

encoded = '%7B%22hoge%22%3A%7B%22foo%22%3A%22bar%22%2C%22hoo%22%3A%22baz%22%7D%7D'
# => {"hoge":{"foo":"bar","hoo":"baz"}}

def manytimes
  100000.times { yield } if block_given?
end

Benchmark.bmbm do |bm|
  bm.report("URI.decode") {
    manytimes { URI.decode encoded }
  }
  bm.report("URI.decode_www_form_component") {
    manytimes { URI.decode_www_form_component encoded }
  }
  bm.report("CGI.unescape") {
    manytimes { CGI.unescape encoded }
  }
end
Ruby 2.1
$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin15.0]

$ ruby decode_bench.rb
Rehearsal -----------------------------------------------------------------
URI.decode                      2.360000   0.020000   2.380000 (  2.404751)
URI.decode_www_form_component   1.270000   0.010000   1.280000 (  1.467851)
CGI.unescape                    1.480000   0.020000   1.500000 (  1.578028)
-------------------------------------------------------- total: 5.160000sec

                                    user     system      total        real
URI.decode                      2.570000   0.030000   2.600000 (  2.729236)
URI.decode_www_form_component   1.170000   0.010000   1.180000 (  1.189314)
CGI.unescape                    1.510000   0.010000   1.520000 (  1.577981)

遅いし obsolete だし、URI.decode はやめておいた方がよさそうだ。 singleton method URI.decode (Ruby 2.4.0)

URI.decode_www_form_component を使うのがいいのかな?

Ruby 2.4

試しに Ruby 2.4 でベンチとってみたら、思わぬ結果になった。

$ ruby --version
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin15]

$ ruby decode_bench.rb
Rehearsal -----------------------------------------------------------------
URI.decode                      2.290000   0.010000   2.300000 (  2.334818)
URI.decode_www_form_component   1.280000   0.020000   1.300000 (  1.330829)
CGI.unescape                    0.120000   0.000000   0.120000 (  0.133207)
-------------------------------------------------------- total: 3.720000sec

                                    user     system      total        real
URI.decode                      2.560000   0.030000   2.590000 (  2.859363)
URI.decode_www_form_component   1.270000   0.010000   1.280000 (  1.329829)
CGI.unescape                    0.120000   0.000000   0.120000 (  0.127875)

CGI.unescape が10倍以上速くなっている。

Ruby 2.4 から CGI モジュールのいくつかのメソッドが C拡張として実装されたらしい。

github.com

escape/unescape, escapeHTML/unescapeHTML 辺りが対象かな?

ruby/escape.c at trunk · ruby/ruby · GitHub

というわけで

URL decode したい時は Ruby 2.4 にして CGI.unescape を使うと速いよ!ということでした。