Procs和lambdas differ关于方法范围以及return
关键字的影响。我对它们之间的性能差异很感兴趣。我写了一个测试,如下所示:
def time(&block)
start = Time.now
block.call
p "that took #{Time.now - start}"
end
def test(proc)
time{(0..10000000).each{|n| proc.call(n)}}
end
def test_block(&block)
time{(0..10000000).each{|n| block.call(n)}}
end
def method_test
time{(1..10000000).each{|n| my_method(n)}}
end
proc1 = Proc.new{|x| x*x}
proc2 = proc{|x| x*x}
lam1 = lambda{|x| x*x}
lam2 = ->x{x*x}
def my_method(x)
x*x
end
test(proc1)
test(proc2)
test(lam1)
test(lam2)
test_block{|x| x*x}
test(method(:my_method))
method_test
此代码的结果如下所示。
"that took 0.988388739"
"that took 0.963193172"
"that took 0.943111226"
"that took 0.950506263"
"that took 0.960760843"
"that took 1.090146951"
"that took 0.644500627"
method(:my_method)
是最慢的,这是因为它检查循环中每次迭代的查找表。
同样,另一项测试如下:
def test2(&block)
time{(0..1000000).each{block.call}}
end
test2{Proc.new{|x| x*x}}
test2{proc{|x| x*x}}
test2{lambda{|x| x*x}}
test2{->(x){x*x}}
返回此结果:
"that took 0.415290453"
"that took 0.378787963"
"that took 0.3888118"
"that took 0.391414639"
Proc.new
是最慢的创建方法,这是因为我们有创建整个对象来包装proc的开销。
我断言procs和lambdas的执行时间是相同的,无论它们的创建方法如何。
答案 0 :(得分:6)
所以看来你有三个问题。中间的一个我不清楚,所以我将解决另外两个:
为什么正常的方法调用要快得多?
这是更容易的问题。
首先要意识到这里涉及的时间是函数调用开销。我根据你的代码做了我自己的计时(但是使用了身份函数而不是乘法),非直接调用的时间延长了49%。通过一次乘法,非直接调用只需要43%的时间。换句话说,你看到很大差异的一个原因是你的功能本身几乎什么也没做。即使是单次乘法也会使差异的6%“消失”。在任何合理复杂度的方法中,方法调用开销通常占总时间的相对较小的百分比。
接下来,请记住,proc / block / lambda本质上是一个被携带的代码块(尽管块文字不能保存到变量中)。这意味着比方法调用更多级别的间接...意味着至少CPU必须遍历指向某个东西的指针。
另外,请记住Ruby支持闭包,我认为在确定间接代码应该运行的环境时会有一些开销。
在我的机器上,运行直接调用函数的C程序比使用指向函数的指针的开销少10%。像Ruby这样的解释性语言,其中也涉及到闭包,肯定会使用更多。
我的测量(Ruby 2.2)表明直接方法调用大约需要6次乘法,间接调用大约需要10次。
因此,虽然开销几乎是其两倍,但请记住,两种情况下的开销通常都相对较小。
在不同的方法之间是否还有其他(基于表现)的理由?
我会说,鉴于上述数据,答案通常是否定的:您最好使用能够提供最易维护代码的构造。
选择其中一个绝对有充分的理由。老实说,我对lambda和块之间的差异感到惊讶(在我的机器上,lambda的开销减少了20%)。 Lambdas创建包含参数列表检查的匿名方法,所以如果有什么我会期望它稍微慢一点,但我的测量结果将它提前了。
直接调用更快,简直就不足为奇了。
这种事情产生影响的地方是非常频繁地称为代码,其中开销在壁挂式的方式中显得更加明显。在这种情况下,做各种丑陋的优化以尝试压缩更快的速度,一直到内联代码(完全避开函数调用开销)是有意义的。
例如,假设您的应用程序使用数据库框架。您对它进行分析并找到一个频繁调用的小型(例如,少于20次乘法运算)函数,该函数将数据从数据库结果复制到数据结构中。这样的函数可能构成了结果处理的最大份额,并简单地内联该函数可能会牺牲一些代码清晰度来节省大量的CPU时间。
简单地将“方形函数”内联到一个包含十亿步的长数值计算中可以节省大量时间,因为操作本身比直接方法调用花费的时间少得多。
但在大多数情况下,使用干净,清晰的代码会更好。