As you know, in Ruby, a block is not an object for some reasons, one of
which is performance.
def f
yield
end
def g &blk
blk.call
end
In g, a block is explicitly converted to a Proc object.
What about in f?
Is a block still converted to a Proc object implicitly?
If it’s not converted, I understand the performance issue.
However, it’s always converted to a Proc object, I don’t understand why
blocks improve performance just because they are not objects.
blk.call
Can somebody explain this, please?
You’ve got the answer already, but it’s interesting to see how much of a
difference it makes: (YMMV, of course.)
Thank you for the benchmark which convinces me of blocks being more
efficient.
blk.call
Can somebody explain this, please?
You’ve got the answer already, but it’s interesting to see how much of a
difference it makes: (YMMV, of course.)
require ‘benchmark’
def outer11(&bl)
inner1(&bl)
end
def outer12(&bl)
inner2(&bl)
end
def outer21
inner1 {yield}
end
def outer22
inner2 {yield}
end
def inner1(&bl)
bl.call
end
def inner2
yield
end
n = 100000
Benchmark.bmbm(10) do |rpt|
rpt.report(“outer11”) do
n.times {outer11{}}
end
rpt.report(“outer12”) do
n.times {outer12{}}
end
rpt.report(“outer21”) do
n.times {outer21{}}
end
rpt.report(“outer22”) do
n.times {outer22{}}
end
end
While to anybody else this code made things clear, for me it is still
a little bit confusing:
why outer12 is performing slower than outer22?
According to prev posts, I have understood that the usage of
block.call requires a conversion to a Proc and this is slower. But in
above case where is this conversion taking place? (or simply, why is
it slower?).
While to anybody else this code made things clear, for me it is still
a little bit confusing:
why outer12 is performing slower than outer22?
According to prev posts, I have understood that the usage of
block.call requires a conversion to a Proc and this is slower. But in
above case where is this conversion taking place? (or simply, why is
it slower?).
def outer12(&bl)
inner2(&bl)
end
The conversion from block to a Proc object happens because of the &bl in
the above definition. When outer12 is actually called, the Proc is
instantiated and the variable bl is bound to it.
Thanks Joel… it looks like my initial understanding was wrong. It is
not block.call the one that triggers block to Proc conversion, but in
fact passing blocks as parameters. Is this the correct understanding?
Thanks Joel… it looks like my initial understanding was wrong. It is
not block.call the one that triggers block to Proc conversion, but in
fact passing blocks as parameters. Is this the correct understanding?
Right, it happens when the block is passed to a method that expects a
“&block” argument (or if the method explicitly calls Proc.new). If you
are executing “block.call”, then “block” is a variable and it, like any
variable, refers to an object.
It’s up to the callee, not the caller, to determine how to treat the
block: either yield to it or turn it into an object. The former has a
small performance advantage (and yield is an elegant notation). The
latter allows the Proc object to be saved away somewhere and called even
after the method has finished.
As you know, in Ruby, a block is not an object for some reasons, one of
which is performance.
I though this had been largely optimized. Can’t ruby refrain from
instatiating the block as a proc until it is needed as such? If so then
simply calling .call on the block reference could just trigger yield.
That would give them nearly the same performance characteristics.
On Jul 1, 2006, at 8:05 PM, Joel VanderWerf wrote:
Alexandru P. wrote:
Thanks Joel… it looks like my initial understanding was wrong.
It is
not block.call the one that triggers block to Proc conversion, but in
fact passing blocks as parameters. Is this the correct understanding?
Right, it happens when the block is passed to a method that expects
a “&block” argument (or if the method explicitly calls Proc.new).
If you are executing “block.call”, then “block” is a variable and
it, like any variable, refers to an object.
I like to compare these situations as ‘explicit block argument’ vs.
‘implicit block argument’. You could also think of it as named vs.
anonymous. In any case, as long as the block remains unnamed/
implicit there is no need to package it up into an explicit object
(i.e. instance of Proc). It is the objectification step that burns
up the extra cycles.
executing “block.call”, then “block” is a variable and it, like any
variable, refers to an object.
I like to compare these situations as ‘explicit block argument’ vs. ‘implicit
block argument’. You could also think of it as named vs. anonymous. In any
case, as long as the block remains unnamed/implicit there is no need to
package it up into an explicit object (i.e. instance of Proc). It is the
objectification step that burns up the extra cycles.
I see it (subtly) differently. The block itself isn’t an argument to
the method; it’s a syntactic construct, more analogous to the argument
list than to a particular argument. In that capacity, it’s always
explicit; that is, it’s written out as part of the method call (by
definition). It’s anonymous, but only in the sense that one could say
an argument list is anonymous; there’s no ArgumentList class for an
argument list to be a named instance of, and no Block class for blocks
to be named instances of, so anonymity is sort of a red herring.
From that perspective, the objectification is entirely layered on top
of the block; it doesn’t change the status of the block per se.