Спочатку треба розібратися з тим що таке 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
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"