Nathan Long
Nathan Long

Reputation: 125942

Why might a proc run faster than a block?

This answer on another question says that

array.map(&:to_s)

is faster than

array.map { |n| n.to_s }

In the first example, & turns :to_s into a Proc. The second example uses a block.

Why might a Proc be faster than a block in that benchmark? Is there some optimization that this technique allows the interpreter to do?

Upvotes: 15

Views: 1259

Answers (3)

Frederick Cheung
Frederick Cheung

Reputation: 84124

As others have been said this is specifically about Symbol#to_proc rather than procs in general and it is almost certainly ruby implementation dependant. Before Symbol#to_proc was in ruby itself, the pure ruby implementations of it were definitely slower the the equivalent block.

For a real answer you'd want to profile ruby while you're executing such a benchmark.

My reading of the ruby source code is that when you call Symbol#to_proc the proc you get is a bit special: The body of the proc is just a C api call (rb_funcall_passing_block), whereas in the other cases it's actual ruby code which takes a little longer to execute.

Upvotes: 4

Daniel Vartanov
Daniel Vartanov

Reputation: 3267

It is not really about "proc vs block".

Here is a simple experiment (feel free to copy and run):

require 'benchmark'

many = 500
array = (1..10000).to_a

proc = proc { |a| a.to_s }

Benchmark.bm do |x|
  x.report('Symbol#to_proc') { many.times { array.map(&:to_s) } }
  x.report('proc') { many.times { array.map(&proc) }  }
  x.report('block') { many.times { array.map { |a| a.to_s } }  }
end

Ruby 1.9.3p194 results:

                user     system      total        real
Symbol#to_proc  1.170000   0.000000   1.170000 (  1.169055)
proc            1.450000   0.000000   1.450000 (  1.454216)
block           1.450000   0.000000   1.450000 (  1.448094)

As you see, block and proc both take virtually the same amount of CPU time. The magic is inside Symbol#to_proc itself.

Upvotes: 7

Simon Chiang
Simon Chiang

Reputation: 895

Just a guess but perhaps it's because the Proc doesn't need to hold onto the context of the call the same way as the block. The block needs to know variables declared outside of it, the Proc does not.

Upvotes: 0

Related Questions