YJIT и производительность Ruby 3+: стоит ли того?

YJIT — это как турбонаддув для вашего Ruby-приложения. Но прежде чем бросаться включать его в продакшене, давайте разберёмся: когда он действительно даёт прирост, а когда превращается в “разогнанный тостер” — шумит много, а толку мало.


� Боль: Ruby и производительность — вечная история

Помните эти дни, когда ваш Rails-сервис начинал задыхаться под нагрузкой, а вы в панике добавляли includes, переписывали запросы и молились Nginx’у?

# Типичный "узкий" участок
User.joins(:posts).where("posts.created_at > ?", 1.week.ago).each do |user|
  user.calculate_loyalty_score # 100500 итераций
end

До Ruby 3.x мы жили в мире, где:

  • MRI (CRuby) — удобен, но медленный,
  • JRuby — быстрый, но с проблемами совместимости,
  • TruffleRuby — магия, но сложная настройка.

И вот появился YJIT — обещание скорости без боли. Но так ли это?


🚀 Что такое YJIT?

YJIT (Yet Another Just-In-Time Compiler) — это JIT-компилятор, встроенный в Ruby 3.1+. В отличие от MJIT (предыдущей реализации), он:

  • Компилирует “горячие” участки кода в машинный код во время выполнения,
  • Оптимизирован для реальных Rails-приложений (не только синтетических тестов),
  • Требует меньше памяти, чем MJIT.

Но главное — обещание ускорения на 10-30% для реальных задач.


🔧 Как включить YJIT?

Проще, чем настроить Sidekiq:

# Запуск Ruby с YJIT
RUBYOPT="--yjit" rails server

# Или в коде
RubyVM::YJIT.enable

Проверим, что он работает:

RubyVM::YJIT.enabled? # => true

🏎️ Тест на практике: YJIT vs без него

Возьмём типичные сценарии:

1. Циклы с бизнес-логикой

def calculate_stats
  users = User.active.limit(10_000)
  users.each { |user| user.update!(score: complex_calculation(user)) }
end

Результаты:

  • Без YJIT: 14.2 сек
  • С YJIT: 11.8 сек (~17% быстрее)

2. Рендеринг JSON API

get "/api/v1/posts" do
  Post.limit(100).to_json
end
  • Без YJIT: 890 RPS
  • С YJIT: 1020 RPS (~15% прирост)

3. ActiveRecord-запросы

User.includes(:posts).where(posts: { likes_count: 100.. }).find_each(&:profile)

Здесь разница всего 2-5% — YJIT почти не влияет на время SQL.


📉 Когда YJIT бесполезен (или вреден)

  1. Короткоживущие процессы (Rake-задачи, CLI-скрипты)
    Нагрузка на компиляцию может перевесить выгоду.

  2. Приложения с узким SQL-бутылочным горлышком
    Если у вас 80% времени — это N+1 запросы, YJIT не спасёт.

  3. Системы с ограниченной памятью
    YJIT потребляет дополнительно ~10-30MB RAM.

  4. Ruby < 3.1
    В ранних версиях он был сырой и мог даже замедлять работу.


🧪 Антипаттерны использования

1. Включение “на всякий случай”

# config/initializers/yjit.rb
RubyVM::YJIT.enable if Rails.env.production?

Проблема: без мониторинга вы не узнаете, помогает ли это.

2. Ожидание чуда

“Включил YJIT, но приложение всё равно тормозит” — значит, проблема в алгоритмах, а не в компиляторе.

3. Использование без обновления Ruby

YJIT в Ruby 3.3 работает значительно лучше, чем в 3.1.


🛠️ Практика: как внедрять правильно

  1. Замеряем до/после (используем benchmark-ips или rbspy):
require 'benchmark/ips'

Benchmark.ips do |x|
  x.report("without YJIT") { some_code }
  x.report("with YJIT") { RubyVM::YJIT.enable; some_code }
end
  1. Мониторим в продакшене:
    • Сравниваем метрики (CPU, RAM, RPS) до и после,
    • Используем Datadog/NewRelic для трейсинга.
  2. A/B-тестируем: Можно запустить часть серверов с YJIT, часть — без.

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

— Какие вы знаете способы ускорить Ruby-код?

— Начиная с Ruby 3.1, YJIT даёт прирост до 30% для CPU-bound задач. Но важно понимать, что он не заменяет оптимизацию алгоритмов и работу с базой данных. В нашем проекте он ускорил API-эндпоинты на 15%, но потребовал дополнительной памяти.


🧮 Вывод: стоит ли?

Да, если:

  • У вас Ruby ≥ 3.1 (лучше 3.3+),
  • Есть CPU-bound задачи (рендеринг, вычисления),
  • Достаточно памяти.

Нет, если:

  • Приложение упирается в I/O (SQL, HTTP-запросы),
  • Работает на слабых серверах,
  • Это разовый скрипт.

P.S. Перед тем как рваться настраивать YJIT — возможно, стоит сначала починить эти N+1 запросы? 😉

🗓 Дата публикации: 20.09.2024, но это не точно...

Ruby YJIT оптимизация производительность JIT-компиляция продакшен