Подтвердить что ты не робот

Почему proc может работать быстрее, чем блок?

Этот ответ по другому вопросу говорит, что

array.map(&:to_s)

быстрее, чем

array.map { |n| n.to_s }

В первом примере & превращает :to_s в Proc. Во втором примере используется блок.

Почему Proc может быть быстрее, чем блок в этом тесте? Есть ли какая-то оптимизация, которую этот метод позволяет интерпретатору делать?

4b9b3361

Ответ 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 не делает.