Ruby 2.0.0 was released a few months back and I finally had some time to look into some of the features and changes that came with it. Lazy collections has always been a cool concept for me and so I decided to do a few benchmarks.

That said, a quick Google search brought me to this great page where someone had already written a usable benchmark back in 2012 when the 2.0.0 preview release came out:

Benchmark (ruby_2_0_0_enum_lazy_bench.rb) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
require 'benchmark'

[10000, 100000, 1000000, 10000000].each do |size|
  Benchmark.bm(15) do |b|
    b.report("chainable #{size}") do
      hashes = (1..size).select(&:even?).map(&:hash).map(&:to_s)
    end
  end

  Benchmark.bm(15) do |b|
    b.report("one iteration #{size}") do
      hashes = (1..size).inject([]) do |accumulator, number|
        if number.even?
          accumulator << number.hash.to_s
        else
          accumulator
        end
      end
    end
  end

  Benchmark.bm(15) do |b|
    b.report("chainable lazy #{size}") do
      hashes = (1..size).lazy.select(&:even?).map(&:hash).map(&:to_s).to_a
    end
  end
end

And then the results, with Ruby 2.0.0p0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
                  user     system      total        real
chainable 10000   0.000000   0.000000   0.000000 (  0.002659)
                  user     system      total        real
one iteration 10000  0.000000   0.000000   0.000000 (  0.002732)
                  user     system      total        real
chainable lazy 10000  0.010000   0.000000   0.010000 (  0.005103)
                  user     system      total        real
chainable 100000  0.030000   0.000000   0.030000 (  0.031249)
                  user     system      total        real
one iteration 100000  0.020000   0.000000   0.020000 (  0.027257)
                  user     system      total        real
chainable lazy 100000  0.060000   0.000000   0.060000 (  0.053770)
                  user     system      total        real
chainable 1000000  0.260000   0.010000   0.270000 (  0.270027)
                  user     system      total        real
one iteration 1000000  0.280000   0.010000   0.290000 (  0.283680)
                  user     system      total        real
chainable lazy 1000000  0.560000   0.010000   0.570000 (  0.566137)
                  user     system      total        real
chainable 10000000  2.830000   0.120000   2.950000 (  2.956604)
                  user     system      total        real
one iteration 10000000  2.910000   0.130000   3.040000 (  3.095763)
                  user     system      total        real
chainable lazy 10000000  5.580000   0.140000   5.720000 (  6.079247)

Conclusion

Turns out that performance of Lazy relative to non-lazy variants hasn’t changed enough for it to be worthwhile to use for anything other than outrageously huge colletions (perhaps many many times larger than 10000000) from a performance point of view. That said, depending on the operation you’re performing, using Lazy might be useful if you want to play within memory constraints.

Comments