WEBrick::HTMLUtils.escape のコードを見て、
str.gsub!(/&/n, '&')
str.gsub!(/\"/n, '"')
str.gsub!(/>/n, '>')
str.gsub!(/</n, '<')
のように 4回の gsub! を呼ぶより
EscapeTable = {
"&" => "&",
'"' => """,
'<' => "<",
'>' => ">",
}
のようなテーブルを作っておいて
str.gsub(/[&"<>]/n) {|x| EscapeTable[x] }
のように変換した方が多少速いかも? と思い、試してみました。
以下は &"<> が含まれる割合を 0, 0.1, 0.2, 0.5, 1, 10, 100パー
セントで変化させて計測した結果です。
&"<> の出現頻度が 0〜0.5%と少ないときはテーブル式の方が速く、
出現頻度が 1%を越えるときは gsub! 連発式の方が速いようです。
% ruby -v
ruby 1.8.2 (2004-11-03) [i386-linux]
% ruby escape-html.rb
user system total real
percentage = 0.00
0.250000 0.010000 0.260000 ( 0.261090)
0.060000 0.000000 0.060000 ( 0.059014)
percentage = 0.10
0.320000 0.000000 0.320000 ( 0.319485)
0.100000 0.000000 0.100000 ( 0.102455)
percentage = 0.20
0.300000 0.020000 0.320000 ( 0.326251)
0.130000 0.010000 0.140000 ( 0.139616)
percentage = 0.50
0.370000 0.000000 0.370000 ( 0.368818)
0.240000 0.030000 0.270000 ( 0.273462)
percentage = 1.00
0.430000 0.000000 0.430000 ( 0.430216)
0.470000 0.020000 0.490000 ( 0.486549)
percentage = 10.00
1.510000 0.020000 1.530000 ( 1.525060)
4.030000 0.450000 4.480000 ( 4.486088)
percentage = 100.00
12.340000 0.000000 12.340000 ( 12.340990)
39.520000 3.840000 43.360000 ( 43.360325)
&"<> がそれほど多く出現しないテキストでは、テーブル式の方が
速く、自分の用途ではその方が適していたので、
WEBrick::HTMLUtils.escape の代わりに、テーブル式を用意して使っ
ています。
WEBrick::HTMLUtils.escape をテーブル式に変更した方がいいかと
いうと、微妙ですね。かといって、2種類の方式を用意するのも、
やりすぎな気がします。
結論らしいものはありませんが、FYI ということで。
require 'benchmark'
def escape_by_gsubs (string)
str = string ? string.dup : ""
str.gsub!(/&/n, '&')
str.gsub!(/\"/n, '"')
str.gsub!(/>/n, '>')
str.gsub!(/</n, '<')
str
end
EscapeTable = {
"&" => "&",
'"' => """,
'<' => "<",
'>' => ">",
}
def escape_by_hash (string)
str = string ? string : ""
str.gsub(/[&"<>]/n) {|x| EscapeTable[x] }
end
def make_test_string (percentage)
ratio = percentage.to_f / 100
len = 10000
string = "o" * (len * (1 - ratio))
string << (0...(len - string.length)).collect { '<&">'[rand(4)].chr }.join
string.split(//).sort_by { rand }.join
end
Benchmark.bm {|x|
[0, 0.1, 0.2, 0.5, 1, 10, 100].each {|percentage|
str = make_test_string(percentage)
n = 1000
printf("percentage = %.2f\n", percentage)
x.report { n.times { escape_by_gsubs(str) } }
x.report { n.times { escape_by_hash(str) } }
puts
}
}