关于方法范围和 return
关键字的影响的Procs和lambdas differ . 我对它们之间的性能差异很感兴趣 . 我写了一个测试,如下所示:
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的执行时间是相同的,无论它们的创建方法如何 .
-
为什么普通方法调用比procs和lambdas(1/3时间减少)快得多?
-
人们是否认为这可能会随着不同的块函数等而改变?
-
在不同的方法之间是否还有其他(基于表现)的理由?
1 回答
所以看来你有三个问题 . 中间的一个我不清楚,所以我将解决另外两个:
Why is normal method invocation so much faster?
这是更容易的问题 .
首先要意识到这里涉及的时间是函数调用开销 . 我根据你的代码做了我自己的计时(但是使用了身份函数而不是乘法),非直接调用的时间延长了49% . 通过一次乘法,非直接调用只需要43%的时间 . 换句话说,你看到很大差异的一个原因是你的功能本身几乎什么也没做 . 即使是单次乘法也会产生6%的差异"vanish" . 在任何合理复杂度的方法中,方法调用开销通常将占总时间的相对较小的百分比 .
接下来,请记住proc / block / lambda本质上是一个被携带的代码块(尽管块文字不能保存到变量中) . 这意味着比方法调用更多级别的间接...意味着至少CPU必须遍历指向某事物的指针 .
另外,请记住Ruby支持闭包,我认为在确定间接代码应该运行的环境时会有一些开销 .
在我的机器上,运行直接调用函数的C程序比使用指向函数的指针的开销少10% . 像Ruby这样的解释性语言,其中也涉及到闭包,肯定会使用更多 .
我的测量(Ruby 2.2)表明直接方法调用大约需要6次乘法,间接调用大约需要10次 .
因此,虽然开销几乎是开发的两倍,但请记住,两种情况下的开销通常都相对较小 .
Are there any other (performance based) reasons to chose between the different approaches?
我会说,鉴于上述数据,答案通常是否定的:您最好使用能够提供最易维护代码的构造 .
绝对有充分理由选择其中一个 . 老实说,我对lambda和块之间的差异感到惊讶(在我的机器上,lambda的开销减少了20%) . Lambdas创建包含参数列表检查的匿名方法,所以如果有什么我会期望它稍微慢一点,但我的测量结果提前了 .
直接调用更快简直就不足为奇了 .
这种事情有所作为的地方是非常频繁地称为代码,其中开销在壁挂式的方式中变得明显 . 在这种情况下,做各种丑陋的优化以尝试压缩更快的速度,一直到内联代码(完全避开函数调用开销)是有意义的 .
例如,假设您的应用程序使用数据库框架 . 您对它进行分析并找到一个频繁调用的小型(例如,少于20次乘法运算)函数,该函数将数据从数据库结果复制到数据结构中 . 这样的函数可能构成了结果处理的最大份额,并简单地内联该函数可能会牺牲一些代码清晰度来节省大量的CPU时间 .
简单地将“平方函数”内联到长达10亿步的数值计算中可以节省大量时间,因为操作本身比直接方法调用花费的时间少得多 .
但在大多数情况下,使用干净,清晰的代码会更好 .