This blog is part of our Ruby 2.5 series.

irb> def greet
irb>   yield
irb> end
  => :greet
irb>
irb> def greet_with_welcome(&block)
irb>   puts 'Welcome'
irb>   greet(&block)
irb> end
  => :greet_with_welcome
irb> greet_with_welcome { p 'BigBinary' }
Welcome
"BigBinary"
  => "BigBinary"

In Ruby 2.4 when we pass a block to a method which further passes to another method, ruby creates a new Proc object by the given block before passing this proc to the another method.

This creates unnecessary objects even when block parameter is not accessed. It also creates a chain of Proc objects when the block parameter is passed through various methods.

Proc creation is one of heavyweight operation because we need to store all of local variables (represented by Env objects in MRI internal) to the heap.

Ruby 2.5 introduced lazy proc allocation. Ruby 2.5 does not create Proc object while passing a block to another method. It rather passes the block information. If the block is accessed somewhere then it creates a Proc object by the given block.

This resulted in lesser memory allocation and faster execution.

Ruby 2.4

irb> require 'benchmark'
  => true
irb> def greet
irb>   yield
irb> end
  => :greet
irb>
irb> def greet_with_welcome(&block)
irb>   puts 'Welcome'
irb>   greet(&block)
irb> end
  => :greet_with_welcome
irb>
irb> Benchmark.measure { 1000.times { greet_with_welcome { 'BigBinary' } } }
Welcome
Welcome
...
...
...
  => #<Benchmark::Tms:0x007fe6ab929de0 @label="", @real=0.022295999999187188, @cstime=0.0, @cutime=0.0, @stime=0.01, @utime=0.0, @total=0.01>

Ruby 2.5

irb> require 'benchmark'
  => true
irb> def greet
irb>   yield
irb> end
  => :greet
irb>
irb> def greet_with_welcome(&block)
irb>   puts 'Welcome'
irb>   greet(&block)
irb> end
  => :greet_with_welcome
irb>
  irb> Benchmark.measure { 1000.times { greet_with_welcome { 'BigBinary' } } }
Welcome
Welcome
...
...
...
  => #<Benchmark::Tms:0x00007fa4400871b8 @label="", @real=0.004612999997334555, @cstime=0.0, @cutime=0.0, @stime=0.001524000000000001, @utime=0.0030690000000000023, @total=0.004593000000000003>

As we can see there is considerable improvement in execution time in Ruby 2.5 when a block param is passed.

Here is relevant commit and discussion.