以下の問題に遭遇して以来、ダイジェスト値を計算する際に Digest(:MD5).hexdigest(str)
と書く様にしていました。autoload かどうかに関わらず…。
単純に毎回 Digest(name)
関数を実行していると、複数回繰り返し呼ばれる様な場合に著しく時間がかかる事を、最近ようやく把握しました…。
require 'digest/md5'
require 'benchmark'
N = 100_000
nums = 1.upto(N).map(&:to_s)
Benchmark.bm(15) do |x|
x.report('Digest::MD5') do
nums.each { |num| Digest::MD5.hexdigest(num) }
end
x.report('memoized') do
digest = Digest(:MD5)
nums.each { |num| digest.hexdigest(num) }
end
x.report('Digest(:MD5)') do
nums.each { |num| Digest(:MD5).hexdigest(num) }
end
end
__END__
user system total real
Digest::MD5 0.170000 0.010000 0.180000 ( 0.182523)
memoized 0.130000 0.000000 0.130000 ( 0.132090)
Digest(:MD5) 8.120000 7.750000 15.870000 ( 42.705663)
Digest(name)
関数の実装は以下の通り。
# https://github.com/ruby/ruby/blob/trunk/ext/digest/lib/digest.rb#L96-L109
def Digest(name)
const = name.to_sym
Digest::REQUIRE_MUTEX.synchronize {
# Ignore autoload's because it is void when we have #const_missing
Digest.const_missing(const)
}
rescue LoadError
# Constants do not necessarily rely on digest/*.
if Digest.const_defined?(const)
Digest.const_get(const)
else
raise
end
end
require 'digest/md5'
してから Digest::MD5.hexdigest(str)
を実行すれば良い話だった気がします。