Forum: Ruby Is a block converted to a Proc object before yield?

Ca0b18ec9e11dc777b2b8084fe5d5f90?d=identicon&s=25 Sam Kong (ssk)
on 2006-06-29 19:57
Hello!

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.

Can somebody explain this, please?

Thanks.

Sam
0ec4920185b657a03edf01fff96b4e9b?d=identicon&s=25 Yukihiro Matsumoto (Guest)
on 2006-06-29 20:06
(Received via mailing list)
Hi,

In message "Re: Is a block converted to a Proc object before yield?"
    on Fri, 30 Jun 2006 02:57:32 +0900, Sam Kong <sam.s.kong@gmail.com>
writes:

|Is a block still converted to a Proc object implicitly?

Not under the current implementation.

							matz.
Ca0b18ec9e11dc777b2b8084fe5d5f90?d=identicon&s=25 Sam Kong (ssk)
on 2006-06-29 20:20
Yukihiro Matsumoto wrote:

> |Is a block still converted to a Proc object implicitly?
>
> Not under the current implementation.
>
> 							matz.

I'm pretty sure that this is a very official answer...:-)

Matz, I want to take this opportunity to thank you for making me a happy
programmer with Ruby.

Sam
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-06-29 20:28
(Received via mailing list)
Sam Kong wrote:
>   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

__END__

Output:

Rehearsal ---------------------------------------------
outer11     0.890000   0.010000   0.900000 (  0.894500)
outer12     0.370000   0.000000   0.370000 (  0.364880)
outer21     0.770000   0.000000   0.770000 (  0.776638)
outer22     0.170000   0.000000   0.170000 (  0.163393)
------------------------------------ total: 2.210000sec

                user     system      total        real
outer11     0.490000   0.000000   0.490000 (  0.491969)
outer12     0.400000   0.000000   0.400000 (  0.396264)
outer21     0.760000   0.000000   0.760000 (  0.764508)
outer22     0.160000   0.000000   0.160000 (  0.161982)
Ca0b18ec9e11dc777b2b8084fe5d5f90?d=identicon&s=25 Sam Kong (ssk)
on 2006-06-29 22:24
Joel VanderWerf wrote:
> Sam Kong wrote:
>>   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.

What a lovely community Ruby has!

Sam
45196398e9685000d195ec626d477f0e?d=identicon&s=25 unknown (Guest)
on 2006-06-30 14:56
(Received via mailing list)
Joel VanderWerf wrote:
> Sam Kong wrote:
> > Hello!
> >
> > 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.

T.
5c4e57ad9f066e56297a60b4a1daa1d3?d=identicon&s=25 Alexandru Popescu (Guest)
on 2006-07-01 03:14
(Received via mailing list)
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?).

thanks (and sorry to be confused),

./alex
--
.w( the_mindstorm )p.
---
(http://themindstorms.blogspot.com)
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-07-01 23:53
(Received via mailing list)
Alexandru Popescu wrote:
 > 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.

 >> def outer22
 >>   inner2 {yield}
 >> end

No & here, so no Proc is created.
5c4e57ad9f066e56297a60b4a1daa1d3?d=identicon&s=25 Alexandru Popescu (Guest)
on 2006-07-02 00:42
(Received via mailing list)
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?

./alex
--
.w( the_mindstorm )p.
---
(http://themindstorms.blogspot.com)
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-07-02 02:07
(Received via mailing list)
Alexandru Popescu 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.

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.
E7559e558ececa67c40f452483b9ac8c?d=identicon&s=25 unknown (Guest)
on 2006-07-03 02:58
(Received via mailing list)
On Jul 1, 2006, at 8:05 PM, Joel VanderWerf wrote:

> Alexandru Popescu 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.

Gary Wright
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-07-03 03:16
(Received via mailing list)
Hi --

On Mon, 3 Jul 2006, gwtmp01@mac.com wrote:

>> 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.


David

--
David A. Black (dblack@wobblini.net)
Ruby Power and Light, LLC (http://www.rubypowerandlight.com)

See what the readers are saying about "Ruby for Rails"!
http://www.rubypowerandlight.com/quotes
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.