Spis treściKliknij link, aby przejść do wybranego miejsca
Ta treść została automatycznie przetłumaczona z ukraińskiego.
Na początku trzeba zrozumieć, czym jest JIT, YJIT i czy jest nam potrzebny. W tej notatce opiszę proces aktualizacji ruby (ponieważ YJIT jest instalowany razem z ruby tylko przy użyciu flagi). Przetestujemy szybkość Ruby i Ruby z YJIT.
Co to jest JIT i YJIT?
JIT (Just-In-Time) kompilacja w Ruby — to technologia, która przekształca bajt-kod Ruby w nattywny kod maszynowy bezpośrednio podczas wykonywania programu. Użycie JIT może znacznie zwiększyć wydajność wykonywania programu, ponieważ natywny kod maszynowy działa szybciej niż interpretowany bajt-kod. Ruby używa maszyny wirtualnej YARV (Yet Another Ruby VM), która przekształca napisany kod Ruby w bajt-kod przed jego wykonaniem. Dodanie JIT do tego procesu pozwala dynamicznie kompilować niektóre części kodu w locie, co przyspiesza działanie kodu i zmniejsza obciążenie serwera.
YJIT - to nowy kompilator JIT, który jest już wbudowany w Ruby od wersji 3.1. YJIT wykorzystuje tzw. "leniwe" JIT-kompilowanie, gdzie kompilacja jest wykonywana tylko dla "gorących" ścieżek wykonania, które są często używane. To zmniejsza koszty kompilacji kodu, który jest rzadko wykonywany, i zapewnia wzrost wydajności tam, gdzie jest to najbardziej potrzebne.
YJIT zwiększa szybkość wykonywania programów bez potrzeby wprowadzania istotnych zmian w samym kodzie. YJIT jest wynikiem próby optymalizacji działania Ruby za pomocą JIT-kompilacji, zachowując przy tym stabilność i zgodność z istniejącymi programami.
Sprawdzamy, czy YJIT jest włączony
Mój lokalny setup - macOS, rbenv i brew. Lokalnie sprawdzamy wersję ruby i czy YJIT jest włączony:
Terminal:
ruby -v ruby 3.2.1
Konsola Rails:
irb(main):003:0> RubyVM::YJIT.enabled?
(irb):3:in `<main>': niezainicjowana stała RubyVM::YJIT (NameError)
RubyVM::YJIT.enabled?
^^^^^^
To znaczy, obecnie Ruby nie ma YJIT.
Mierzymy szybkość wykonywania Ruby bez YJIT
Stworzymy skrypt, który będzie liczył ciąg Fibonacciego. Trochę googlowałem i zapytałem ChatGPT, jak najlepiej zrobić prosty benchmarking i to jest najczęstsza opcja.
Tworzymy plik benchmark.rb z kodem:
require 'benchmark'
def fibonacci(n)
return n if n <= 1
fibonacci(n - 1) + fibonacci(n - 2)
end
puts Benchmark.measure {
fibonacci(40)
}
Uruchamiamy:
ruby ~/Desktop/benchmark.rb
I mój wynik obecnie (bez YJIT):
8.309469 0.000711 8.310180 (8.320920)
Instalujemy Ruby z YJIT
Obecnie mam ruby 3.2.1 (rbenv). Muszę zainstalować tę samą wersję, ale już z YJIT. Zaczynamy.
Disclaimer: Każdy ma swój setup. Podany przykład to tylko mój przypadek.
Oficjalna dokumentacja mówi, że YJIT wymaga:
- Kompilatora C, takiego jak GCC lub Clang
- GNU Make i Autoconf
- Kompilator Rust rustc i Cargo (jeśli chcesz budować w trybie dev/debug)
- Wersja Rust musi być >= 1.58.0.
Z tego wszystkiego muszę tylko zainstalować Rust (Fajnie, prawda? Język programowania Rust jest używany jako kompilator).
Instalujemy Rust za pomocą brew:
brew install rust
To może zająć dość dużo czasu (~10min).
W .zshrc trzeba dodać następującą flagę:
export RUBY_CONFIGURE_OPTS="--enable-yjit"
Bez flagi Ruby zostanie zainstalowane bez YJIT (domyślnie jest wyłączony). To ważny punkt.
Restartujemy terminal, aby załadował RUBY_CONFIGURE_OPTS.
Instalujemy Ruby (technicznie, rbenv install 3.2.1 będzie wykonywane z flagą --enable-yjit):
\W $ rbenv install 3.2.1 rbenv: /Users/user/.rbenv/versions/3.2.1 już istnieje kontynuować instalację? (y/N) y
Po instalacji sprawdzamy, czy YJIT działa oraz benchmarki wykonania pliku benchmark.rb.Jest tu pewien szczegół. YJIT, nawet jeśli jest zainstalowany razem z Ruby - jest wyłączony.
Dodaj tę flagę, aby ruby uruchamiał się z YJIT:
export RUBY_YJIT_ENABLE=true
Następnie powinniśmy zobaczyć +YJIT przy sprawdzaniu ruby -v
ruby -v ruby 3.2.1 (2023-02-08 revision 31819e82c8) +YJIT [x86_64-darwin23]
irb RubyVM::YJIT.enabled? => true
YJIT działa. Teraz testujemy.
ruby ~/Desktop/benchmark.rb
I otrzymujemy:
1.624351 0.000245 1.624596 (1.626251)
To znaczy, różnica w szybkości wykonywania skryptu wynosi 5,11 razy:
8.309469/1.624351=5,1155624616
Technicznie, to dość duża i znacząca aktualizacja w szybkości Ruby. Włączyłem tylko jedną opcję, na poziomie kompilacji uzyskujemy bardzo potężną optymalizację. YJIT jest już gotowy do produkcji i może być stosowany na serwerach produkcyjnych, na przykład dla aplikacji Ruby on Rails. Na przykład heroku oferuje włączenie YJIT za pomocą jednej komendy:
heroku config:set RUBYOPT="--enable-yjit"
Ten post nie ma jeszcze żadnych dodatków od autora.