Rescuing blocks?

On a lark, I did this, and was shocked at the result. Why the massive
difference in performance between “begin; end” within a block, and “{ }”
within a block?

I may do some more tinkering with this later on.

require ‘benchmark’

N = 10_000_000

Benchmark.bmbm do |b|
b.report ‘begin’ do
N.times { begin; rescue; end }
end

b.report ‘begin no rescue’ do
N.times { begin; end }
end

b.report ‘no begin nested’ do
N.times { { } }
end

b.report ‘plain’ do
N.times { }
end
end

Rehearsal ---------------------------------------------------
begin 3.547000 0.000000 3.547000 ( 3.656000)
begin no rescue 2.797000 0.000000 2.797000 ( 2.860000)
no begin nested 38.062000 0.453000 38.515000 ( 40.593000)
plain 1.735000 0.000000 1.735000 ( 1.782000)
----------------------------------------- total: 46.594000sec

                   user     system      total        real

begin 3.516000 0.000000 3.516000 ( 3.594000)
begin no rescue 2.750000 0.000000 2.750000 ( 2.828000)
no begin nested 36.422000 0.297000 36.719000 ( 38.797000)
plain 1.750000 0.000000 1.750000 ( 1.781000)

Pat: I think it already does and there’s still a performance hit.
Though Eric’s machine chugs a little more than mine, I’d be interested
to see what his ratios are with this added benchmark:

require ‘benchmark’

N = 10_000_000

Benchmark.bmbm do |b|
b.report ‘begin’ do
N.times { begin; rescue; end }
end

b.report ‘begin no rescue’ do
N.times { begin; end }
end

b.report ‘no begin’ do
N.times { }
end
end

G:\Documents and Settings\Dan Nugent\Desktop>ruby -v rescue.rb
ruby 1.8.4 (2005-12-24) [i386-mswin32]
Rehearsal ---------------------------------------------------
begin 3.703000 0.000000 3.703000 ( 3.844000)
begin no rescue 3.125000 0.000000 3.125000 ( 3.281000)
no begin 2.078000 0.000000 2.078000 ( 2.172000)
------------------------------------------ total: 8.906000sec

                  user     system      total        real

begin 4.703000 0.000000 4.703000 ( 4.906000)
begin no rescue 3.203000 0.000000 3.203000 ( 3.312000)
no begin 2.047000 0.000000 2.047000 ( 2.141000)

G:\Documents and Settings\Dan Nugent\Desktop>

Daniel N. wrote:

N.times { begin; rescue; end }

end

b.report ‘begin no rescue’ do
N.times { begin; end }
end

b.report ‘no begin’ do
N.times { }
end
end

On my machine, WinXP Home SP2, 512mb RAM, Athlon 1900 (or so). Both
benchmarks run within irb.

Using Eric’s original code:

Rehearsal --------------------------------------------
begin 3.578000 0.000000 3.578000 ( 3.625000)
no begin 1.782000 0.000000 1.782000 ( 1.828000)
----------------------------------- total: 5.360000sec

            user     system      total        real

begin 3.547000 0.000000 3.547000 ( 3.625000)
no begin 1.796000 0.000000 1.796000 ( 1.812000)

100% increase between the two. Not as much as Eric’s, but still
significant I would think.

Using Daniel’s code:

Rehearsal ---------------------------------------------------
begin 3.546000 0.000000 3.546000 ( 3.625000)
begin no rescue 2.829000 0.000000 2.829000 ( 2.875000)
no begin 1.781000 0.000000 1.781000 ( 1.828000)
------------------------------------------ total: 8.156000sec

                   user     system      total        real

begin 3.547000 0.000000 3.547000 ( 3.609000)
begin no rescue 2.812000 0.000000 2.812000 ( 2.891000)
no begin 1.735000 0.000000 1.735000 ( 1.797000)

Interesting middle ground there. So begin/end blocks have their own
overhead, above and beyond the exception handlers.

Note that I am not a benchmarking expert, and in fact am merely guessing
at the relative importance of the numbers, assuming the “real” values
are the important ones. I probably should read more about this module.

DÅ?a Streda 15 Február 2006 20:03 Dave C. napísal:

On a lark, I did this, and was shocked at the result. Why the massive
difference in performance between “begin; end” within a block, and “{ }”
within a block?

Because it’s a literal hash constructor, not a nested block?

On a slightly related note, the literal hash constructor is apparently
faster
than Hash.new. Good to know.

David V.

On Feb 15, 2006, at 2:03 PM, Dave C. wrote:

N.times { { } }

This is equivalent to N.times { Hash.new }

Hi –

On Mon, 13 Feb 2006, David V. wrote:

How would you parse the difference whether the raise applies to the whole
block, or just the previous statement?

I think Daniel’s idea would be to have it be the same as with method
bodies.

David


David A. Black ([email protected])
Ruby Power and Light (http://www.rubypowerandlight.com)

“Ruby for Rails” chapters now available
from Manning Early Access Program! Ruby for Rails