Rubyと遅延初期化
そういえばベンチマークは公開してなかったと思うので置いておきます.
ソースコード
# 手軽にソースコードを置いておける場所が欲しいなぁ.
# No.0 class NormalHello def initialize @message = Time::now.strftime("Hello @ %Y-%m-%d") end attr_reader :message end # No.1 class LaterInitializationHello def message unless @message then @message = Time::now.strftime("Hello @ %Y-%m-%d") end @message end end # No.2 class LaterInitializationHello2 def message @message ||=Time::now.strftime("Hello @ %Y-%m-%d") @message end end # No.3 class LaterInitializationSelfOverrideHello def message unless @message then @message = Time::now.strftime("Hello @ %Y-%m-%d") end def self.message @message end @message; end end # No.4 class LaterInitializationSelfOverrideUsingAttrReaderHello attr_reader :message alias :message_by_attr_reader :message def message unless @message then @message = Time::now.strftime("Hello @ %Y-%m-%d") end class << self alias :message :message_by_attr_reader end @message; end end class Module alias original_attr_reader attr_reader def attr_reader(*names) unless block_given? return original_attr_reader(*names) end names.each do |name| ivar = "@#{name}" define_method(name) do value = instance_variable_get(ivar) unless value value = yield self instance_variable_set(ivar, value) end class << self; self; end.class_eval do original_attr_reader name end value end end end end # No.5 class LaterInitializationSelfOverrideUsingAttrReaderDefinedByMethodHello attr_reader(:message) { Time::now.strftime("Hello @ %Y/%m/%d") } end if $0 == __FILE__ then require 'benchmark' LOOP_COUNT= 4000000 targets = [] targets << NormalHello::new targets << LaterInitializationHello::new targets << LaterInitializationHello2::new targets << LaterInitializationSelfOverrideHello::new targets << LaterInitializationSelfOverrideUsingAttrReaderHello::new targets << LaterInitializationSelfOverrideUsingAttrReaderDefinedByMethodHello::new Benchmark.bm() { |x| targets.each_with_index(){ |obj, index| x.report(index.to_s){ LOOP_COUNT.times{ obj.message } } } } end
実行結果
user system total real 0 4.090000 0.930000 5.020000 ( 5.012927) 1 7.640000 1.870000 9.510000 ( 9.515122) 2 8.050000 2.050000 10.100000 ( 10.107144) 3 7.030000 1.840000 8.870000 ( 8.874788) 4 3.950000 1.170000 5.120000 ( 5.118873) 5 3.960000 1.080000 5.040000 ( 5.040693)
当然遅延初期化なしのNo.0が一番速いですが最終案のNo.5との差は僅かです.
No.2はRubyで遅延初期化を書く場合の一般的なイディオムですが一番遅いです.一般的なイディオムを利用したコードは可読性が上ってよいのですが…