Skip to content
← Wszystkie wpisy
4 min czytania Dawid Skłodowski

Ruby 3.1: YJIT ląduje i nowy debugger

Ruby 3.1 przybywa w Boże Narodzenie z YJIT — nowym JIT od Shopify, który wreszcie przyspiesza realne obciążenia Rails, nie tylko benchmarki — plus nowoczesny debugger, lepsze komunikaty błędów i wygodną składnię. Praktyczny przegląd.

Ruby 3.1.0 przybyło w Boże Narodzenie, a nagłówkiem jest ten, na który czekałem, odkąd JIT pierwszy raz się pojawił w 2.6: JIT, który faktycznie pomaga Railsom. Wcześniejszy MJIT był prawdziwą inżynierią, ale, jak odnotowałem wtedy, mało robił dla typowych obciążeń webowych zdominowanych przez I/O i dyspozycję metod. YJIT, wniesiony przez Shopify i dostarczany w 3.1, to zmienia. Obok niego przybywa naprawdę dobry debugger, znacznie lepsze komunikaty błędów i miła składnia. Oto przegląd.

YJIT: JIT, który pomaga prawdziwym aplikacjom

Wielką wiadomością jest YJIT (Yet Another Ruby JIT), nowy kompilator just-in-time od Shopify zbudowany inną techniką (lazy basic-block versioning) niż stary MJIT. Kluczową różnicą jest ta, która ma znaczenie w praktyce: tam, gdzie MJIT pomagał głównie benchmarkom związanym z CPU, YJIT dostarcza realne przyspieszenia na faktycznych obciążeniach Rails — mocno dyspozycjonującym metody, mocno alokującym kodzie, który produkcyjne aplikacje webowe faktycznie uruchamiają. Shopify zbudowało go właśnie dlatego, że potrzebowali, by ich ogromny monolit Rails działał szybciej, i działa, na produkcji, na realnym ruchu.

ruby --yjit my_app.rb        # zgłoś się do YJIT
# albo RUBY_YJIT_ENABLE=1 / konfiguracja w aplikacji

To historia JIT wreszcie opłacająca się ludziom czytającym te wpisy. Zastrzeżenie z 2.6 — „JIT jeszcze nie pomaga Railsom, obserwuj to miejsce” — jest rozwiązane: YJIT to coś, co możesz faktycznie włączyć dla aplikacji Rails i zmierzyć znaczącą poprawę (raportowane dwucyfrowe procentowe przyspieszenia na realnych obciążeniach), z większą ilością w drodze, gdy dojrzeje. Wciąż jest młody w 3.1 i szybko się poprawia, ale to moment, w którym wieloletnie pchnięcie wydajności Ruby dociera do aplikacji webowych, które większość z nas buduje.

Nowoczesny debugger: debug.gem

Drugim wyróżnikiem jest nowy gem debug, teraz dołączony i rekomendowany debugger. Latami debugowanie Ruby oznaczało byebug, pry albo rozsypywanie puts; nowy debug to nowoczesny debugger od podstaw, który jest szybki, pełnofunkcyjny i integruje się z edytorami (zwłaszcza UI debugowania VS Code) oraz zdalnym debugowaniem:

require "debug"

def calculate(order)
  debugger      # wpada tu w potężną, szybką sesję debuggera
  # step, next, breakpointy, watchpointy, zdalne podłączenie…
end

Wspiera rzeczy, które poważny debugger powinien — breakpointy (w tym warunkowe i watchpointy), krokowanie, inspekcję ramek i podłączanie do działającego procesu zdalnie — i jest zauważalnie szybszy niż starsze narzędzia. Posiadanie dobrego, oficjalnego debuggera w bibliotece standardowej (ta sama trajektoria co odświeżenie irb) to realna poprawa jakości życia i warta przejścia z byebug/pry.

error_highlight: błędy wskazujące miejsce

Mały, lecz zachwycający dodatek: error_highlight sprawia, że komunikaty błędów wskazują dokładną część linii, która spowodowała problem, nie tylko linię:

NoMethodError: undefined method `name' for nil
    user.profile.name
         ^^^^^^^^

Gdy user.profile jest nil, błąd podkreśla profile.name, więc widzisz natychmiast, która część łańcuchowego wyrażenia była nil — zamiast gapić się na długą linię, zastanawiając się, która . wybuchła. Na ruchliwej linii z kilkoma wywołaniami metod to zamienia moment mrużenia oczu w natychmiastową diagnozę. Jest włączone domyślnie i to rodzaj drobiazgu, który po cichu oszczędza czas debugowania codziennie.

Przyjemności składniowe

Kilka dodatków składniowych poprawiających jakość życia:

  • Pominięcie wartości literału hasha. Gdy klucz hasha pasuje do nazwy zmiennej lokalnej, możesz pominąć wartość — { x:, y: } znaczy { x: x, y: y }:

    name = "Ada"; age = 36
    person = { name:, age: }      # => { name: "Ada", age: 36 }

    To drobiazg usuwający częsty kawałek powtórzenia, zwłaszcza przy budowaniu hashy ze zmiennych, które właśnie obliczyłeś.

  • Pinowanie wyrażeniami w pattern matchingu. Operator pin przyjmuje teraz wyrażenia: in { count: ^(a + b) }, czyniąc pattern matching (teraz ustaloną funkcję) bardziej elastycznym.

  • Usprawnienia IRB trwają — autouzupełnianie i wyświetlanie dokumentacji czynią konsolę jeszcze milszą, budując na odświeżeniu z 2.7.

Czy aktualizować?

Tak — i to wydanie z naprawdę ekscytującym powodem ponad zwykłe. YJIT to długo wyczekiwany JIT, który pomaga prawdziwym aplikacjom Rails; możesz go włączyć i zmierzyć realną poprawę, a będzie tylko lepszy. Nowy gem debug jest wart przyjęcia ponad twój stary debugger za szybkość i integrację z edytorem. error_highlight i przyjemności składniowe to darmowa, addytywna jakość życia. A ponieważ duża zmiana łamiąca (argumenty nazwane) była w 3.0, skok z 3.0 do 3.1 jest gładki.

Łuk od podglądu JIT z 2.6 do tu jest satysfakcjonujący do prześledzenia: podróż wydajności „3×3” dała Ruby szybszy runtime, ale JIT konkretnie nie pomagał aplikacjom webowym aż do teraz — a 3.1 to miejsce, gdzie to wreszcie ląduje, dzięki firmie, która uruchamia jedną z największych aplikacji Rails na świecie potrzebując tego. Silne wydanie i stosowne, by zamknąć rok.