Improving performance using Enumerator::Lazy
Tue 07 Aug 2018

One of the performance matter we usually encounter is Enumerator. Today, I introduce you a kind of performance issue we ever faced about enumerator.

That's Enumerator in ruby. The case is when we enumerate a large range. For instance, (1..1000_000). That's when we just need to use first 100 elements but in fact, system perform all of 1.000.000 elements. It's not necessary, is it?

The solution we usually see about improving performance is lazy load. We can refer the lazy load for images: https://github.com/verlok/lazyload

Diving deep in ruby library we can see:

Below is the Lazy class in rubystub 2.3 for your reference. Lazy class provided methods to support Lazy load.

class Lazy < Enumerator
end

module Enumerable
   def lazy()
        #This is a stub, used for indexing
   end
end

class Range
    include Enumerable
end

Enumerable provided lazy method returned Lazy instance. Making it convenient for us to use with Range:

irb(main):001:0> (1..1000_000).class
=> Range

Thus, our code is:

(1..1000_000).lazy.map {|n| n * 2}.first(100)

Performing below code to see the comparison between normal enumerator and lazy enumerator:

require  'benchmark/ips'

Benchmark.ips do |x|
  x.config(:time => 5, :warmup => 2)

  x.report("Enumerations") do
    (1..1000_000).map {|n| n * 2}.first(100)
  end

  x.report("Lazy Enumerations") do
    (1..1000_000).lazy.map {|n| n * 2}.first(100)
  end

  x.compare!
end

Result:

➜  performance git:(master) ✗ ruby enum.rb             
Warming up --------------------------------------
        Enumerations    11.000  i/100ms
   Lazy Enumerations     9.730k i/100ms
Calculating -------------------------------------
        Enumerations    118.665  (± 5.9%) i/s -    594.000  in   5.024046s
   Lazy Enumerations    100.515k (± 4.8%) i/s -    505.960k in   5.045457s

Comparison:
   Lazy Enumerations:   100515.4 i/s
        Enumerations:      118.7 i/s - 847.05x  slower

➜  performance git:(master) ✗ ruby enum.rb
Warming up --------------------------------------
        Enumerations     1.000  i/100ms
   Lazy Enumerations     2.675k i/100ms
Calculating -------------------------------------
        Enumerations     12.166  (± 8.2%) i/s -     61.000  in   5.028548s
   Lazy Enumerations     27.269k (± 3.6%) i/s -    136.425k in   5.009590s

Comparison:
   Lazy Enumerations:    27269.0 i/s
        Enumerations:       12.2 i/s - 2241.44x  slower