直观地说,后者应该比前者更快.但是,当我看到基准测试结果时,我感到非常惊讶:
require 'benchmark/ips' b = (0..20).to_a; y = 21; Benchmark.ips do |x| x.report('<<') { a = b.dup; a << y } x.report('+=') { a = b.dup; a += [y] } x.report('push') { a = b.dup; a.push(y) } x.report('[]=') { a = b.dup; a[a.size]=y } x.compare! end
结果是:
Calculating ------------------------------------- << 24.978k i/100ms += 30.389k i/100ms push 24.858k i/100ms []= 22.306k i/100ms ------------------------------------------------- << 493.125k (± 3.2%) i/s - 2.473M += 599.830k (± 2.3%) i/s - 3.009M push 476.374k (± 3.3%) i/s - 2.386M []= 470.263k (± 3.8%) i/s - 2.364M Comparison: +=: 599830.3 i/s <<: 493125.2 i/s - 1.22x slower push: 476374.0 i/s - 1.26x slower []=: 470262.8 i/s - 1.28x slower
然而,当我的一位同事独立创建自己的基准时,结果恰恰相反:
Benchmark.ips do |x| x.report('push') {@a = (0..20).to_a; @a.push(21)} x.report('<<') {@b = (0..20).to_a; @b << 21} x.report('+=') {@c = (0..20).to_a; @c += [21]} x.compare! end
结果:
Calculating ------------------------------------- push 17.623k i/100ms << 18.926k i/100ms += 16.079k i/100ms ------------------------------------------------- push 281.476k (± 4.2%) i/s - 1.410M << 288.341k (± 3.6%) i/s - 1.457M += 219.774k (± 8.3%) i/s - 1.093M Comparison: <<: 288341.4 i/s push: 281476.3 i/s - 1.02x slower +=: 219774.1 i/s - 1.31x slower
我们还跨越了我们的基准测试,在我们的两台机器上,他的基准测试显示+=
速度明显慢于<<
我的基准测试,而我的基准测试显示相反.
这是为什么?
UPD:我的Ruby版本是Ruby 2.2.3p173(2015-08-18修订版51636)[x86_64-darwin14] ; 我的同事是2.2.2(不知道详情,明天会更新帖子).
UPD2:ruby 2.2.2p95(2015-04-13修订版50295)[x86_64-darwin12.0]我的队友的Ruby版本.
在我看来,为了简化各种运算符的比较,我们应该删除不必要的代码并保持测试简单.
require 'benchmark/ips' y = 10 Benchmark.ips do |x| x.report('<<') { a = [0,1,2,3,4,5,6,7,8,9]; a << y } x.report('+=') { a = [0,1,2,3,4,5,6,7,8,9]; a += [y] } x.report('push') { a = [0,1,2,3,4,5,6,7,8,9]; a.push(y) } x.report('[]=') { a = [0,1,2,3,4,5,6,7,8,9]; a[a.size]=y } x.compare! end
上述代码的结果与问题中共享的第二个代码段一致.
Calculating ------------------------------------- << 101.735k i/100ms += 104.804k i/100ms push 92.863k i/100ms []= 99.604k i/100ms ------------------------------------------------- << 2.134M (± 3.3%) i/s - 10.682M += 1.786M (±13.2%) i/s - 8.804M push 1.930M (±16.1%) i/s - 9.472M []= 1.948M (± 7.9%) i/s - 9.761M Comparison: <<: 2134005.4 i/s []=: 1948256.8 i/s - 1.10x slower push: 1930165.3 i/s - 1.11x slower +=: 1785808.5 i/s - 1.19x slower [Finished in 28.3s]
为什么<<
比快+=
?
Array#<<
将元素附加到数组的四种方法中最快的是因为它只是这样 - 将一个元素附加到数组.相反,Array#+
附加一个元素但返回一个新的数组副本 - 创建新的数组副本使其最慢.(可以使用toogle code
文档中的选项来了解某些方法所做的其他工作)
基准标记 dup
如果我们使用下面的代码进行基准测试,
require 'benchmark/ips' y = 10 Benchmark.ips do |x| x.report('<<') { a = [0,1,2,3,4,5,6,7,8,9].dup; a << y } x.report('+=') { a = [0,1,2,3,4,5,6,7,8,9].dup; a += [y] } x.report('push') { a = [0,1,2,3,4,5,6,7,8,9].dup; a.push(y) } x.report('[]=') { a = [0,1,2,3,4,5,6,7,8,9].dup; a[a.size]=y } x.compare! end
我们看到以下结果:
Calculating ------------------------------------- << 65.225k i/100ms += 76.106k i/100ms push 64.864k i/100ms []= 63.582k i/100ms ------------------------------------------------- << 1.221M (±14.3%) i/s - 6.001M += 1.291M (±13.1%) i/s - 6.393M push 1.164M (±14.1%) i/s - 5.773M []= 1.168M (±14.5%) i/s - 5.722M Comparison: +=: 1290970.6 i/s <<: 1221029.0 i/s - 1.06x slower []=: 1168219.3 i/s - 1.11x slower push: 1163965.9 i/s - 1.11x slower [Finished in 28.3s]
如果我们仔细研究两个结果,我们只看到一个区别.该+=
条目已成为第一个,而其余方法的顺序与原始结果保持一致.
使用时为什么会翻转结果dup
?
这是我疯狂的猜测,我猜测Ruby解释器优化了代码并且没有创建新数组作为其中的一部分,+=
因为它知道它正在处理新创建的数组副本dup