← strona głównaProgramowanie (Програмування)

Włączamy YJIT w Ruby 3.2.1 (Ruby on Rails)

Włączamy YJIT w Ruby 3.2.1 (Ruby on Rails). Czym jest YJIT i po co go włączać?

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"

🔥 Więcej postów

Wszystkie wpisy
ZOMBIE w Ruby. Co to jest?
Programowanie (Програмування)3 maj '24 12:41

ZOMBIE w Ruby. Co to jest?

ZOMBIE w Ruby. Co to jest? Do czego używany jest ten termin w repozytorium Ruby?

Programowanie (Програмування)7 maj '24 07:24

Czym jest natywny kod maszynowy?

Co to jest natywny kod maszynowy? Jak działa i do czego jest potrzebny?

Co oznacza "Nattywny"?
Terminy (Терміни)22 maj '24 07:01

Co oznacza "Nattywny"?

Czym jest natywny? Dowiedz się, co oznacza termin "natywny" z przykładami użycia w różnych dziedz...