Почему proc может работать быстрее, чем блок?
Этот ответ по другому вопросу говорит, что
array.map(&:to_s)
быстрее, чем
array.map { |n| n.to_s }
В первом примере &
превращает :to_s
в Proc. Во втором примере используется блок.
Почему Proc может быть быстрее, чем блок в этом тесте? Есть ли какая-то оптимизация, которую этот метод позволяет интерпретатору делать?
Ответы
Ответ 1
Как говорили другие, речь идет, в частности, о Symbol#to_proc
, а не о процессах вообще, и это почти наверняка связано с реализацией Ruby. До того, как Symbol#to_proc
был в рубине, чистые рубиновые реализации его были определенно медленнее эквивалентного блока.
Для реального ответа вы хотите профилировать ruby во время выполнения такого теста.
Мое чтение исходного кода ruby заключается в том, что при вызове Symbol#to_proc
полученный вами бит является немного особенным: тело proc - это просто вызов C api (rb_funcall_passing_block
), тогда как в других случаях это фактический код ruby, который занимает немного больше времени для выполнения.
Ответ 2
Речь идет не о "proc vs block".
Вот простой эксперимент (не стесняйтесь копировать и запускать):
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:
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)
Как вы видите, block
и proc
имеют практически одинаковое количество процессорного времени. Магия находится внутри Symbol#to_proc
.
Ответ 3
Просто догадайтесь, но, возможно, потому, что Proc не нужно удерживать контекст вызова так же, как и блок. Блок должен знать переменные, объявленные вне него, Proc не делает.