I need to pass a block from one method to another (I want to call Rails.cache.fetch with block passed to my method).
I can either add &block to parameter list and use that to pass it to the next method, or I can create a new block and call yield inside of it. I've wrote a short example and benchmark:
require "benchmark"
def with_block(&block)
do_something 'Test', &block
end
def with_yield
do_something('Test') { yield }
end
def do_something(string)
"#{yield} #{string}"
end
n = 5_000_000
Benchmark.bmbm do |x|
x.report("&block") do
n.times { with_block { "Yo" } }
end
x.report("yield") do
n.times { with_yield { "Yo" } }
end
end
&block 3.320000 0.010000 3.330000 ( 3.340438)
yield 1.670000 0.000000 1.670000 ( 1.669504)
--------------------------------- total: 5.000000sec
user system total real
&block 3.270000 0.010000 3.280000 ( 3.275914)
yield 1.680000 0.000000 1.680000 ( 1.682768)
Looks like { yield } approach is much faster. Is it the right way to go? Are there any gotchas I'm not aware of because of calling yield inside a newly created block?
Short answer: Always use yield, unless you have a good reason to explicitly reference &block.
See: Why blocks make ruby methods 439% slower
With &block, you get a reified Proc on which you can do all kinds of stuff and which you can move around. However, with a yield and an implicit block, you are limited to only calling the block.
By using yield, the interpreter can bypass all the Proc reification as it knows the developer won't be able to use it; hence it can keep just a C-level structure instead of having to set up a Ruby-level object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With