Обкладинка нотатки: Вмикаємо YJIT у Ruby 3.2.1 (Ruby on Rails)

Вмикаємо YJIT у Ruby 3.2.1 (Ruby on Rails)

Спочатку треба розібратися з тим що таке JIT, YJIT та чи воно нам треба. В цій нотатці я опишу процес оновлення ruby (бо YJIT інсталюється разом з ruby лише при використанні прапорця). Протестуємо швидкість Ruby та Ruby з YJIT.

Що таке JIT та YJIT?

JIT (Just-In-Time) компіляція в Ruby — це технологія, яка перетворює байт-код Ruby у нативний машинний код безпосередньо під час виконання програми. Використання JIT може значно підвищити продуктивність виконання програми, оскільки нативний машинний код виконується швидше, ніж інтерпретований байт-код. Ruby використовує віртуальну машину YARV (Yet Another Ruby VM), яка трансформує написаний код Ruby у байт-код перед його виконанням. Додавання JIT до цього процесу дозволяє динамічно компілювати деякі частини коду на льоту, що пришвидшує роботу коду та зменшує навантаження на сервер.
YJIT - це новий JIT компілятор, який вже вбудований в Ruby починаючи з версії 3.1. YJIT використовує так звану "ліниву" JIT-компіляцію, де компіляція виконується лише для "гарячих" шляхів виконання, які виконуються часто. Це знижує витрати на компіляцію коду, який виконується рідко, та забезпечує приріст продуктивності там, де це найбільш необхідно.
YJIT підвищує швидкість виконання програм без потреби в істотних змінах в самому коді. YJIT став результатом спроби оптимізувати роботу Ruby за допомогою JIT-компіляції, зберігаючи при цьому стабільність і сумісність з існуючими програмами.

Перевіряємо чи ввімкнен YJIT

Мій локальний сетап - macOS, rbenv та brew. Локально перевіряємо версію ruby та чи ввімкнутий YJIT:
Термінал:
ruby -v

ruby 3.2.1
Rails Console:
irb(main):003:0> RubyVM::YJIT.enabled?
(irb):3:in `<main>': uninitialized constant RubyVM::YJIT (NameError)
                                                          
RubyVM::YJIT.enabled?                                     
      ^^^^^^
Тобто, наразі Ruby немає YJIT.

Заміряємо швидкість виконання Ruby без YJIT

Зробимо скрипт, який буде рахувати послідовність Фібоначчі. Я трохи погуглив і попитав ChatGPT, як краще зробити простий бенчмаркінг і це найпоширеніший варіант.
Створюємо файл benchmark.rb з кодом:
require 'benchmark'

def fibonacci(n)
  return n if n <= 1
  fibonacci(n - 1) + fibonacci(n - 2)
end

puts Benchmark.measure {
  fibonacci(40)
}
Запускаємо:
ruby ~/Desktop/benchmark.rb
І мій результат наразі (без YJIT):
 8.309469   0.000711  8.310180 (8.320920)

Встановлюємо Ruby з YJIT

Наразі я маю ruby 3.2.1 (rbenv). Мені треба встановити цю саму версію, але вже з YJIT. Тож почнемо.
Дисклеймер:
Кожен має свій сетап. Наведений приклад - лише мій кейс.
Офіційна документація каже, що YJIT потребує:
  • A C compiler such as GCC or Clang
  • GNU Make and Autoconf
  • The Rust compiler rustc and Cargo (if you want to build in dev/debug mode)
    • The Rust version must be >= 1.58.0.
З цього всього я маю лише встановити Rust (Прикольно, так. Мова програмування Rust використовуються в якості компілятора).
Встановлюємо Rust за допомогою brew:
brew install rust
На це може піти доволі багато часу (~10хв).
В .zshrc треба додати наступний прапорець:
export RUBY_CONFIGURE_OPTS="--enable-yjit"
Без прапорцю Ruby буде встановлено без YJIT (за замовчуванням він вимкненний). Це важливий момент.
Перезапускаємо термінал, щоб він підтягнув RUBY_CONFIGURE_OPTS
Встановлюємо Ruby (технічно, rbenv install 3.2.1 буде виконуватись з прапорцем --enable-yjit):
\W $ rbenv install 3.2.1
rbenv: /Users/user/.rbenv/versions/3.2.1 already exists
continue with installation? (y/N) y
Після встановлення перевіряємо чи працює YJIT та  бенчмарки виконання файлу benchmark.rb.
Тут є нюанс. YJIT навіть якщо встановленно разом з Ruby - вимкненний.
Додайте цей прапорець, щоб ruby запускався з YJIT:
export RUBY_YJIT_ENABLE=true
Потім ми маємо побачити +YJIT при перевірці ruby -v
ruby -v
ruby 3.2.1 (2023-02-08 revision 31819e82c8) +YJIT [x86_64-darwin23]
irb
RubyVM::YJIT.enabled?
=> true

YJIT працює. Далі тестуємо.

ruby ~/Desktop/benchmark.rb
І отримуємо:
1.624351 0.000245 1.624596 (1.626251)
Тобто різниця в швидкості виконання скрипту 5,11 разів:
8.309469/1.624351=5,1155624616
Технічно, це доволі великий та значний апгрейд в швидкості Ruby. Ввімкнув лише одну опцію, на рівні компіляції ми отримуємо дуже потужну оптимізацію. YJIT вже production-ready й може застосовуватись на продакшн серверах, наприклад для Ruby on Rails додатків. Наприклад heroku пропонує включити YJIT лише за допомогою однієї команди:
heroku config:set RUBYOPT="--enable-yjit"

🙌 Підтримати блог @memecode

Ви можете поширити цей допис у соцмережах, чим допоможете платформі цейво розвиватись (* ^ ω ^)

📝 Більше публікацій:
Дисклеймер

Інформація на сайті tseivo.com є суб'єктивною та відображає особисті погляди та досвід авторів та авторок блогів.

Використовуйте цей ресурс як одне з декількох джерел інформації під час своїх досліджень та прийняття рішень. Завжди застосовуйте критичне мислення. Людина сама несе відповідальність за свої рішення та дії.