Ruby Meta Programming: define_method or class_eval

今天刚巧看到Matt Aimonetti的blog上讨论ruby meta programming的执行效率问题,就跟着做了一下,还是学到了不少东西。大致说一下吧,首先Matt写了一个简单的计算时间的DSL类:
module TimeDSL
    def second
      self * 1
    end
    alias_method :seconds, :second

    def minute
      self * 60
    end
    alias_method :minutes, :minute

    def hour
      self * 3600
    end
    alias_method :hours, :hour

    def day
      self * 86400
    end
    alias_method :days, :day

    def week
      self * 604800
    end
    alias_method :weeks, :week

    def month
      self * 2592000
    end
    alias_method :months, :month

    def year
      self * 31471200
    end
    alias_method :years, :year
end
Numeric.send :include, TimeDSL


这个类很简单,就是为Numeric类增加了与时间相关的一些方法,这样就可以使用类似的代码计算时间了,例如:
1.hours + 20.minutes # => 一小时20分钟是多少秒
3.days + 5.hours # => 3天零5小时
2.years + 10.months # => 两年十个月


代码很简单,不过请大家注意Matt使用
Numeric.send :include, TimeDSL
方法为Numeric类增加了TimeDSL这个模块。
然后他又写了一个利用define_method方式实现TimeDSL的类(为了方便说明,我略作了修改)
module BadMetaTimeDSL

    {:second => 1, 
     :minute => 60, 
     :hour => 3600, 
     :day => [24,:hours], 
     :week => [7,:days], 
     :month => [30,:days], 
     :year => [364.25, :days]}.each do |meth, amount|
		define_method "b_#{meth}" do
			amount = amount.is_a?(Array) ? amount[0].send(amount[1]) : amount
			self * amount
		end
		alias_method "b_#{meth}s".intern, "b_#{meth}"
    end
  end
Numeric.send :include, BadMetaTimeDSL


他发布了这两个类benchmark的测试数据,并得出结论说ruby的meta programming基本上要比正常方式慢3倍左右(我自己的测试数据是2倍左右)
这个结论很快就引来了很多人的的讨论,最后Wycats提出了改进方案
module GoodMetaTimeDSL
  SECOND  = 1
  MINUTE  = SECOND * 60
  HOUR    = MINUTE * 60
  DAY     = HOUR * 24
  WEEK    = DAY * 7
  MONTH   = DAY * 30
  YEAR    = DAY * 364.25

  %w[SECOND MINUTE HOUR DAY WEEK MONTH YEAR].each do |const_name|
      meth = const_name.downcase
      class_eval <<-RUBY
        def g_#{meth}
          self * #{const_name}
        end
        alias g_#{meth}s g_#{meth}
      RUBY
  end
end
Numeric.send :include, GoodMetaTimeDSL

新的类执行效率直逼正常方式定义的类,可以说基本上没有太大的差异了。究其原因define_method需要创建Proc才能执行,而class_eval和ruby自身执行方式没有什么差别,都是直接解释执行。

最后的结论是:如果可能,尽量使用class_eval进行meta programming

不知道怎么把附件的图贴进来,大家看附件吧,也包括源代码。
整个测试都在jruby 1.1.3下通过

可以参考 http://railsontherun.com/2008/6/18/about-metaprogramming-speed

你可能感兴趣的:(Blog,Ruby,jruby)