Оптимизация производительности в Ruby требует глубокого понимания как языка, так и конкретного контекста применения. Вот комплексный подход, который я использую в своей практике.
Золотое правило: Не оптимизируйте без измерений!
benchmark/ips
- для микро-бенчмарковstackprof
- для CPU профилированияmemory_profiler
- для анализа использования памятиrack-mini-profiler
- для веб-приложенийПример бенчмарка:
require 'benchmark/ips'
Benchmark.ips do |x|
x.report("string concat") { "foo" + "bar" }
x.report("string interpolate") { "#{'foo'}#{'bar'}" }
x.compare!
end
# Плохо: создает новый массив на каждой итерации
def process_items(items)
items.map { |i| i.to_s }.join(',')
end
# Лучше: избегаем промежуточных массивов
def process_items(items)
items.each_with_object('') { |i, s| s << i.to_s << ',' }.chop
end
Пример решения:
# Плохо: N+1 запросов
@posts = Post.limit(10)
@posts.each { |p| puts p.author.name }
# Хорошо: eager loading
@posts = Post.includes(:author).limit(10)
Set
вместо массива для поискаlazy
для больших коллекцийПример:
# Плохо: O(n) поиск
if ['admin', 'moderator'].include?(user.role)
# ...
end
# Лучше: O(1) поиск
if Set['admin', 'moderator'].include?(user.role)
# ...
end
#freeze
Пример:
# До оптимизации
100.times { "constant_string".upcase }
# После оптимизации
CONST_STRING = "constant_string".freeze
100.times { CONST_STRING.upcase }
Thread
для I/O-bound задачProcess.fork
для CPU-bound задачRactor
(в Ruby 3+)Пример:
# Последовательная обработка
results = urls.map { |u| fetch_data(u) }
# Параллельная обработка
results = urls.map do |u|
Thread.new { fetch_data(u) }
end.map(&:value)
RubyVM::MJIT
@instance_variables
Пример мемоизации:
def expensive_calculation
@result ||= begin
# сложные вычисления
end
end
Настройки окружения:
RUBY_GC_HEAP_INIT_SLOTS=80000
RUBY_GC_HEAP_FREE_SLOTS=60000
RUBY_GC_HEAP_GROWTH_FACTOR=1.1
Резюмируем: Оптимизация Ruby-кода требует системного подхода: сначала измеряем, затем анализируем, и только потом оптимизируем. Фокусируйтесь на bottleneck'ах, используйте правильные инструменты и помните, что читаемость кода часто важнее микрооптимизаций. Для максимального эффекта сочетайте высокоуровневые оптимизации (алгоритмы, архитектура) с низкоуровневыми (настройки GC, работа с памятью).