Interesting, thanks Colin.
As you said, the code you demonstrated, could performer better, meanwhile
it's somewhat less elegant.
IMHO the code is ok, we just have more space inside the
interpreter/runtime to get improved, like in JVM, the JIT compiler could
compile all of the 4 lines of code in to the same machine instructions and
yield the same performance.
The dynamic nature and elegance design of Ruby certainly sacrificed
performance at somewhat level, but that doesn't stop you and me from using
it, right?
Anyway, thanks for the benchmark demo, I just ran it on my machine, with
both MRI and JRuby,
MRI 1.8.7
user system total real
each_with_index.inject(0) 4.930000 0.000000 4.930000 ( 5.036288)
inject(0) 3.088000 0.000000 3.088000 ( 3.080176)
each_with_index 2.668000 0.000000 2.668000 ( 2.668153)
each 1.700000 0.000000 1.700000 ( 1.713098)
JRuby 1.5.6
user system total real
each_with_index.inject(0) 1.918000 0.000000 1.918000 ( 1.918000)
inject(0) 1.058000 0.000000 1.058000 ( 1.058000)
each_with_index 0.979000 0.000000 0.979000 ( 0.979000)
each 0.624000 0.000000 0.624000 ( 0.624000)
Like your result the raw each runs the fastest, but we can also find that,
the result of each_with_index.inject with JRuby is already close to that of
raw each with MRI.
I'm curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach. (Which usually(?) takes
about the same number of characters for the code.)
ary = [3, 4, 5]
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v * index }
p rrr #=> 14
index = -1; rrr = ary.inject(0) { |sum, v| index += 1; sum + v * index }
p rrr #=> 14
rrr = 0; ary.each_with_index { |v, index| rrr += v * index }; rrr
p rrr #=> 14
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }; rrr
p rrr #=> 14
require "benchmark"
kt = 500_000
Benchmark.bmbm() do |bm|
bm.report( "each_with_index.inject(0)" ) { kt.times {
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v * index }
} }
bm.report( "inject(0)" ) { kt.times {
index = -1; rrr = ary.inject(0) { |sum, v| index += 1; sum + v * index }
} }
bm.report( "each_with_index" ) { kt.times {
rrr = 0; ary.each_with_index { |v, index| rrr += v * index }
} }
bm.report( "each" ) { kt.times {
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }
} }
end
#=>
Rehearsal -------------------------------------------------------------
each_with_index.inject(0) 2.410000 0.000000 2.410000 ( 2.404242)
inject(0) 1.000000 0.000000 1.000000 ( 1.000677)
each_with_index 0.840000 0.010000 0.850000 ( 0.846287)
each 0.640000 0.000000 0.640000 ( 0.640142)
---------------------------------------------------- total: 4.900000sec
user system total real
each_with_index.inject(0) 2.380000 0.020000 2.400000 ( 2.392590)
inject(0) 1.000000 0.000000 1.000000 ( 0.999785)
each_with_index 0.850000 0.000000 0.850000 ( 0.846178)
each 0.640000 0.000000 0.640000 ( 0.639366)
(My apologies if the benchmarks are ragged and unaligned.)