Что работает быстрее в Ruby: блок, процесс или лямбда?

Почему важно различать Proc и процесс ОС, стоимость создания и вызова, а также накладные расходы и общее время параллельной задачи.

Короткий ответ

Что ответить на собеседовании

Сначала уточните, имеется ли в виду Proc или процесс ОС. Блок, обычный Proc и lambda выполняют Ruby-код, но отличаются формой передачи, проверкой аргументов и return; универсального рейтинга скорости нет. Запуск процесса и обмен данными обычно гораздо дороже локального вызова. Однако процессы могут параллельно выполнять независимые CPU-задачи, поэтому итоговое время иногда меньше. Сравнивать нужно одинаковую работу и отдельно учитывать создание исполнителей.

Подробный разбор

Ответ с пояснениями

Сначала уточню слово «процесс»: это процесс операционной системы или имелся в виду Proc? Такие варианты нельзя объединять в один безусловный рейтинг скорости.

Блок передаётся методу и может вызываться через yield. Proc — объект, представляющий блок вместе с захваченным окружением. Лямбда тоже является объектом Proc, но с другой семантикой: она строже проверяет число аргументов, а return завершает саму лямбду. У обычного proc return пытается выйти из охватывающего метода; после завершения этого метода такой выход может вызвать LocalJumpError. Документация Proc.

Небольшой пример показывает формы вызова, но не измеряет скорость:

def apply(value)
  yield value
end

increment = ->(value) { value + 1 }

puts apply(10) { |value| value + 1 } # 11
puts increment.call(10)             # 11

У yield и Proc#call могут различаться накладные расходы. Результат зависит от реализации и версии Ruby, оптимизаций, захвата окружения и того, создаётся ли объект заново при каждой операции. Сравнение заранее созданной лямбды с кодом, который каждый раз создаёт новую, отвечает на другой вопрос. Для корректного измерения также отделяют полезное вычисление от вывода в консоль и проверяют одинаковый результат.

Если речь именно о процессе ОС, Process.fork создаёт дочерний процесс, а system запускает команду и ожидает её завершения. Здесь появляются затраты запуска, планирования, передачи данных и сбора результатов — обычно значительно больше локального вызова. Process.fork, Kernel.system.

Однако независимые тяжёлые вычисления процессы могут выполнять параллельно на разных ядрах. Поэтому большие накладные расходы не доказывают худшее итоговое время. Время одного вызова не равно времени всей задачи. Сначала выбирают подходящие семантику и модель выполнения, затем измеряют одинаковую нагрузку, отдельно учитывая запуск и повторное использование исполнителей.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку