May 22, 2018
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 the 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 a heavyweight operation because we need to store all local variables (represented by Env objects in MRI internal) in the heap.
Ruby 2.5 introduced a lazy proc allocation. Ruby 2.5 will not create a Proc
object when passing a block to another method. Insead, it will pass the block
information. If the block is accessed somewhere else, then it creates a Proc
object by the given block.
This results in lesser memory allocation and faster execution.
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>
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 when a block param is passed in Ruby 2.5.
Here is the relevant commit and discussion.
If this blog was helpful, check out our full blog archive.