首页 文章

proc,Proc.new,lambda和stabby lambda之间的速度差异

提问于
浏览
8

关于方法范围和 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 回答

  • 6

    所以看来你有三个问题 . 中间的一个我不清楚,所以我将解决另外两个:

    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亿步的数值计算中可以节省大量时间,因为操作本身比直接方法调用花费的时间少得多 .

    但在大多数情况下,使用干净,清晰的代码会更好 .

相关问题