Is there any way to pass further the "hidden" block?

C

Chiyuan Zhang

Like this:

def foo
yield
end

def bar(&brk)
foo(&brk)
end

bar { puts "foo" }

What I want to know is: is there any way to define `bar' like this:

def bar
foo
end

so that foo will get the block passed to bar? I want to know this
because I learn from
http://www.pluralsight.com/blogs/dbox/archive/2006/05/09/23068.aspx
that the implicit block is much faster than explicitly passing it
as &brk . I know one solution is

def bar
foo { yield }
end

but that create another block, not the original one.
 
L

Lee Jarvis

Im not sure I fully understand..

But..

def foo(baz)
baz
end

def bar
foo(yield)
end

bar { 'fubar' }
#=> fubar

Perhaps?


Regards,
Lee
 
M

MonkeeSage

Like this:

def foo
yield
end

def bar(&brk)
foo(&brk)
end

bar { puts "foo" }

What I want to know is: is there any way to define `bar' like this:

def bar
foo
end

so that foo will get the block passed to bar? I want to know this
because I learn fromhttp://www.pluralsight.com/blogs/dbox/archive/2006/05/09/23068.aspx
that the implicit block is much faster than explicitly passing it
as &brk . I know one solution is

def bar
foo { yield }
end

but that create another block, not the original one.

The article you mention is not loading for me (nor is googles cached
version), so I'm not sure exactly what you mean. When you pass a block
as a parameter, it's just an explicit reference to the implicit block,
not a new copy:

def bar(&blk_2)
puts blk_2.object_id
end
def foo(&blk_1)
puts blk_1.object_id
bar(&blk_1)
end
puts "implicit block"
foo { "baz" }
puts "explicit block"
prc = lambda { "baz" }
puts prc.object_id
foo(&prc)

# =>
implicit block
-605770738
-605770738
explicit block
-605770758
-605770758
-605770758

Although it is slightly slower to grab a reference to the block, it's
probably not anything you have to worry about. E.g., it's only half as
slow over 100,000 calls for this benchmark:

require "benchmark"
def implicit_blk
yield "baz"
end
def explicit_blk(&blk)
blk.call("baz")
end
n = 100_000
Benchmark.bm(10) { | x |
x.report("implicit") { n.times { implicit_blk { | y | y } } }
x.report("explicit") { n.times { explicit_blk { | y | y } } }
}

# =>
user system total real
implicit 0.370000 0.080000 0.450000 ( 0.478676)
explicit 1.200000 0.090000 1.290000 ( 1.324176)

....for me, that's not anything to be concerned about.

Edit: Ah, the cached version of the article you mention just loaded.
Looks like the author got a larger variance in his benchmark. I just
ran the above benchmark again for 1,000,000 calls (like the article),
with these results:

user system total real
implicit 3.520000 0.640000 4.160000 ( 5.230353)
explicit 12.180000 0.850000 13.030000 ( 15.966038)

....so indeed, passing an explicit reference to the block seems to be
exponentially slower. However, still, 3 times slower over a million
calls still doesn't bother me very much. YMMV.

Regards,
Jordan
 
T

Trans

Like this:

def foo
yield
end

def bar(&brk)
foo(&brk)
end

bar { puts "foo" }

What I want to know is: is there any way to define `bar' like this:

def bar
foo
end

so that foo will get the block passed to bar? I want to know this
because I learn fromhttp://www.pluralsight.com/blogs/dbox/archive/2006/05/09/23068.aspx
that the implicit block is much faster than explicitly passing it
as &brk . I know one solution is

def bar
foo { yield }
end

but that create another block, not the original one.

Do you mean, is there a keyword #block to go along with #block_given?
and #yield? As far as I know, there is no way to pass the block except
via an explicit reference.

But I think explicit may be the future. I'm pretty sure I've heard
some chatter about getting rid of #block_given? Technically I would
think it possible to all but eliminate the speed difference between
explicit and implicit --3x seems very high.

T.
 
M

MonkeeSage

Do you mean, is there a keyword #block to go along with #block_given?
and #yield? As far as I know, there is no way to pass the block except
via an explicit reference.

But I think explicit may be the future. I'm pretty sure I've heard
some chatter about getting rid of #block_given? Technically I would
think it possible to all but eliminate the speed difference between
explicit and implicit --3x seems very high.

T.

On further testing it appears that in situations where can create a
proc and reuse it, the performance comes much closer (although the
implicit version says the same?!):

require "benchmark"
def implicit_blk
yield "baz"
end
def explicit_blk(&blk)
blk.call("baz")
end
n = 1_000_000
prc = lambda { | y | y }
Benchmark.bm(10) { | x |
x.report("implicit") { n.times { implicit_blk(&prc) } }
x.report("explicit") { n.times { explicit_blk(&prc) } }
}

# =>
user system total real
implicit 3.730000 0.520000 4.250000 ( 4.479818)
explicit 6.000000 0.580000 6.580000 ( 6.763514)

....wonder why?

Regards,
Jordan
 
M

MonkeeSage

Hmmm...another thing...passing prc to #explicit_blk as an lval rather
than invoking block_pass shaves another second off the time...

def explicit_blk(blk)
blk.call("baz")
end
n = 1_000_000
prc = lambda { | y | y }
Benchmark.bm(10) { | x |
x.report("explicit") { n.times { explicit_blk(prc) } }
}

# =>
user system total real
explicit 5.030000 0.520000 5.550000 ( 5.774020)

....so I'm curious about two matters:

1.) Why does creating a Proc explicitly and passing it as a block_arg
rather than using a literal block (see previous post) speed up the
#explicit_blk benchmark 2 times, but doesn't have very much effect the
#implicit_blk benchmark?

2.) Why is it less expensive to pass a Proc as an lval rather than as
a block_arg?

Anybody know?

Regards,
Jordan
 
G

Gary Wright

def bar
foo { yield }
end

but that create another block, not the original one.

This is the only technique I'm aware of. I it
is still faster than explicitly reifying the outer block
before calling foo.

Somewhat related: Super will pass along an implicit
block. To disable this do:

super(a, b, &nil)

Gary Wright
 
C

Chiyuan Zhang

Hmm... what i'm looking for is just like super. As all you mentioned
above, maybe there's no such feature yet. :)
 
M

MonkeeSage

Hmmm...another thing...passing prc to #explicit_blk as an lval rather
than invoking block_pass shaves another second off the time...

def explicit_blk(blk)
blk.call("baz")
end
n = 1_000_000
prc = lambda { | y | y }
Benchmark.bm(10) { | x |
x.report("explicit") { n.times { explicit_blk(prc) } }

}

# =>
user system total real
explicit 5.030000 0.520000 5.550000 ( 5.774020)

...so I'm curious about two matters:

1.) Why does creating a Proc explicitly and passing it as a block_arg
rather than using a literal block (see previous post) speed up the
#explicit_blk benchmark 2 times, but doesn't have very much effect the
#implicit_blk benchmark?

2.) Why is it less expensive to pass a Proc as an lval rather than as
a block_arg?

Anybody know?

Regards,
Jordan

Bump
 
R

Rick DeNatale

Hmmm...another thing...passing prc to #explicit_blk as an lval rather
than invoking block_pass shaves another second off the time...

def explicit_blk(blk)
blk.call("baz")
end
n = 1_000_000
prc = lambda { | y | y }
Benchmark.bm(10) { | x |
x.report("explicit") { n.times { explicit_blk(prc) } }
}

# =>
user system total real
explicit 5.030000 0.520000 5.550000 ( 5.774020)

...so I'm curious about two matters:

1.) Why does creating a Proc explicitly and passing it as a block_arg
rather than using a literal block (see previous post) speed up the
#explicit_blk benchmark 2 times, but doesn't have very much effect the
#implicit_blk benchmark?

because you've moved the expensive proc creation outside of the benchmark loop.
2.) Why is it less expensive to pass a Proc as an lval rather than as
a block_arg?

It's not, actually the other way, only slightly:

require "benchmark"

def explicit_blk(blk)
blk.call("baz")
end
n = 1_000_000
prc = lambda { | y | y }
Benchmark.bm(10) { | x |
x.report("outside") { n.times { explicit_blk(prc) } }
x.report("lval") { n.times { prc = lambda { | y | y }; explicit_blk(prc) } }
x.report("parm") { n.times { explicit_blk(lambda { | y | y }) } }
}

user system total real
outside 1.430000 0.010000 1.440000 ( 1.558680)
lval 5.460000 0.030000 5.490000 ( 5.885967)
parm 5.320000 0.030000 5.350000 ( 5.748286)
 
M

MonkeeSage

because you've moved the expensive proc creation outside of the benchmark loop.

But that didn't seem to make very much difference with the implicit
block using the yield keyword, Only with the explicit block passed via
formal parameter (see above). That's the reason for my puzzlement --
creating the Proc outside the loop only seems to benefit when
explicitly passing it via formal parameter, but not when passing it
implicitly using the yield keyword. That seems strange to me.
It's not, actually the other way, only slightly:

Err...I was referring to passing the object "prc" rather than the
reference "&prc"...the former being faster than the later. Still
curious about the reason for the difference in speed.
require "benchmark"

def explicit_blk(blk)
blk.call("baz")
end
n = 1_000_000
prc = lambda { | y | y }
Benchmark.bm(10) { | x |
x.report("outside") { n.times { explicit_blk(prc) } }
x.report("lval") { n.times { prc = lambda { | y | y }; explicit_blk(prc) } }
x.report("parm") { n.times { explicit_blk(lambda { | y | y }) } }

}

user system total real
outside 1.430000 0.010000 1.440000 ( 1.558680)
lval 5.460000 0.030000 5.490000 ( 5.885967)
parm 5.320000 0.030000 5.350000 ( 5.748286)

Regards,
Jordan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top