Reputation: 125942
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
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
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
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