3

在 RubyMonk 中进行练习时(付费墙后面的链接,因此未提供),为了衡量定义方法的性能,eval提供define_method了以下代码:

require 'benchmark'

class Monk
  eval "def zen; end"

  define_method(:zen_block) {}
end

monk = Monk.new

Benchmark.bmbm do |x|
  x.report("eval zen: ") { 1_000_000.times { monk.zen } }
  x.report("define_method zen: ") { 1_000_000.times { monk.zen_block } }
end

作为 Ruby 的新手,我的问题是:方法zenzen_block解释器何时实际“编译”(不确定它是否是正确的词)?似乎不太可能在每次调用时都重新定义zen和。zen_block从我目前所了解的情况来看,在我看来,衡量性能,正确的方法是:

require 'benchmark'

class Monk
  def with_eval
    eval "def zen; end"
  end

  def with_define_method
    self.class.send(:define_method,:zen_block) {}
  end
end

Benchmark.bmbm do |x|
  x.report("eval zen: ") { 1_000_000.times { monk.with_eval } }
  x.report("define_method zen: ") { 1_000_000.times { monk.with_define_method } }
end

第一个块在我的机器中产生这些结果(我已将迭代次数提高到 100 万次以使时间更加稳健):

Rehearsal -------------------------------------------------------
eval zen:             0.070000   0.000000   0.070000 (  0.074196)
define_method zen:    0.120000   0.000000   0.120000 (  0.118621)
---------------------------------------------- total: 0.190000sec

第二个区块的结果(我的建议):

Rehearsal -------------------------------------------------------
eval zen:             7.740000   0.000000   7.740000 (  7.743741)
define_method zen:    1.620000   0.000000   1.620000 (  1.617666)
---------------------------------------------- total: 9.360000sec
4

2 回答 2

3

您从 RubyMonk 中展示的基准测试并没有衡量使用or定义方法的速度。它正在测量调用结果方法的速度。这就是为什么它读取它的方式。evaldefine_method

由于在您了解更多关于 Ruby 解释器的实现之前对您来说可能并不明显的原因,通过eval或定义的方法的速度define_method通常是不一样的。

于 2013-02-25T16:33:15.940 回答
3

感谢您向我们指出这一点。

您是对的,该主题应该措辞如下:“比较由 eval 创建的动态方法与由 define_method 创建的方法的性能”。

我们将在今天晚些时候修复我们内容中的这个错误。

尽管定义方法的性能增量是一个重要的考虑因素,但我们从未看到它具有多大的实际意义(至少在 ruby​​ 中),尤其是因为 AST 将被缓存。

光辉

RubyMonk 团队

于 2013-02-25T18:42:34.697 回答