Ruby 3.3.0 veröffentlicht
- Version Ruby 3.3.0 wurde veröffentlicht. Eingeführt werden der neue Parser Prism, der Parser-Generator Lrama, der in reinem Ruby geschriebene JIT-Compiler RJIT sowie insbesondere Leistungsverbesserungen bei YJIT.
Prism-Parser
- Prism ist ein portabler, fehlertoleranter und wartungsfreundlicher rekursiver Abstiegparser für die Ruby-Sprache und wird als Standard-gem bereitgestellt.
- Prism ist für den produktiven Einsatz geeignet, wird aktiv gewartet und kann als Ersatz für Ripper verwendet werden.
- Es steht eine ausführliche Dokumentation zur Verwendung von Prism zur Verfügung.
- Prism ist sowohl eine C-Bibliothek, die intern in CRuby verwendet wird, als auch ein Ruby-gem, das in allen Tools eingesetzt werden kann, die Ruby-Code parsen müssen.
- Zu den wichtigsten Methoden der Prism-API gehören
Prism.parse(source), Prism.parse_comments(source) und Prism.parse_success?(source).
- Beiträge sind möglich, indem direkt Pull Requests oder Issues im Prism-Repository eingereicht werden.
- Wer den Prism-Compiler experimentell nutzen möchte, kann
ruby --parser=prism oder RUBYOPT="--parser=prism" verwenden, dies sollte jedoch nur zu Debugging-Zwecken geschehen.
Lrama-Parser-Generator
- Bison wurde durch den LALR-Parser-Generator Lrama ersetzt.
- Wer sich dafür interessiert, kann die Zukunftsvision für den Ruby-Parser nachlesen.
- Zur besseren Wartbarkeit wurde der interne Lrama-Parser durch einen von Racc erzeugten LR-Parser ersetzt.
- Unterstützung für parametrisierte Regeln (?, *, +), vorgesehen für die Verwendung in Ruby
parse.y.
YJIT
- Es gibt wesentliche Leistungsverbesserungen gegenüber Ruby 3.2.
- Die Unterstützung für splat- und rest-Argumente wurde verbessert.
- Für Stack-Operationen der virtuellen Maschine werden Register zugewiesen.
- Mehr Aufrufe mit optionalen Argumenten werden kompiliert. Auch Exception-Handler werden kompiliert.
- Nicht unterstützte Aufruftypen und megamorphe Call Sites fallen nicht länger auf den Interpreter zurück.
- Basis-Methoden wie
#blank? in Rails und das spezielle #present? werden inline verarbeitet.
Integer#*, Integer#!=, String#!=, String#getbyte, Kernel#block_given?, Kernel#is_a?, Kernel#instance_of?, Module#=== und weitere wurden gezielt optimiert.
- Die Kompiliergeschwindigkeit ist etwas höher als in Ruby 3.2.
- In Optcarrot ist es mehr als dreimal schneller als der Interpreter.
- Der Speicherverbrauch wurde im Vergleich zu Ruby 3.2 deutlich verbessert.
- Metadaten des kompilierten Codes benötigen wesentlich weniger Speicher.
--yjit-call-threshold wird für Anwendungen mit mehr als 40.000 ISEQ automatisch von 30 auf 120 angehoben.
--yjit-cold-threshold wurde hinzugefügt und überspringt die Kompilierung ausgekühlter ISEQ.
- Auf Arm64 wird kompakterer Code erzeugt.
- Code-GC ist standardmäßig deaktiviert.
--yjit-exec-mem-size wird als hartes Limit behandelt, bei dessen Erreichen keine neue Code-Kompilierung mehr erfolgt.
- Es gibt keinen Performance-Verlust durch Code-GC, und bei Servern, die mit Pitchfork reforken, zeigt sich ein besseres Copy-on-Write-Verhalten.
- Bei Bedarf kann Code-GC mit
--yjit-code-gc aktiviert werden.
- Mit
RubyVM::YJIT.enable kann YJIT zur Laufzeit aktiviert werden.
- Rails 7.2 wird YJIT auf diese Weise standardmäßig aktivieren.
- Diese Methode kann genutzt werden, um YJIT erst nach abgeschlossenem Boot-Vorgang der Anwendung zu aktivieren.
- Wer YJIT beim Start deaktivieren und dennoch andere YJIT-Optionen nutzen möchte, kann
--yjit-disable verwenden.
- Standardmäßig werden mehr YJIT-Statistiken bereitgestellt.
yjit_alloc_size und mehrere Metadaten-bezogene Statistiken werden standardmäßig ausgegeben.
- Die durch
--yjit-stats erzeugte Statistik ratio_in_yjit ist in Release-Builds verfügbar. Spezielle Statistik- oder Development-Builds sind nicht mehr erforderlich.
- Weitere Profiling-Funktionen wurden hinzugefügt.
--yjit-perf wurde ergänzt, um Profiling mit Linux perf zu erleichtern.
--yjit-trace-exits unterstützt Sampling mit --yjit-trace-exits-sample-rate=N.
- Umfangreichere Tests und zahlreiche Bugfixes wurden durchgeführt.
RJIT
- Der in reinem Ruby geschriebene JIT-Compiler RJIT wurde eingeführt und ersetzt MJIT.
- RJIT wird nur auf Unix-Plattformen mit x86-64-Architektur unterstützt.
- Anders als MJIT benötigt es zur Laufzeit keinen C-Compiler.
- RJIT ist ausschließlich für experimentelle Zwecke gedacht.
- In Produktionsumgebungen sollte weiterhin YJIT verwendet werden.
- Wer sich für die Entwicklung von Ruby-JIT interessiert, sollte sich den Vortrag von k0kubun am dritten Tag der RubyKaigi ansehen.
M:N-Thread-Scheduler
- Ein M:N-Thread-Scheduler wurde eingeführt.
- Da M Ruby-Threads von N nativen Threads (Betriebssystem-Threads) verwaltet werden, sinken die Kosten für Erzeugung und Verwaltung von Threads.
- Der M:N-Thread-Scheduler kann die Kompatibilität mit C-Erweiterungen beeinträchtigen und ist deshalb im Main Ractor standardmäßig deaktiviert.
- Mit der Umgebungsvariable
RUBY_MN_THREADS=1 kann M:N-Threading im Main Ractor aktiviert werden.
- In nicht-Main-Ractors ist M:N-Threading immer aktiviert.
- Die Umgebungsvariable
RUBY_MAX_CPU=n setzt die Obergrenze für N, also die maximale Anzahl nativer Threads. Standard ist 8.
- Da pro Ractor nur ein Ruby-Thread ausgeführt werden kann, nutzen Single-Ractor-Anwendungen (die meisten Anwendungen) nur einen nativen Thread.
- Zur Unterstützung blockierender Operationen können mehr native Threads als N verwendet werden.
Leistungsverbesserungen
defined?(@ivar) wurde mithilfe von Object Shapes optimiert.
- Namensauflösung wie bei
Socket.getaddrinfo kann nun unterbrochen werden, sofern in der jeweiligen Umgebung pthreads verfügbar sind.
- Es gibt mehrere Performance-Verbesserungen am Garbage Collector.
- Wenn junge Objekte von alten Objekten referenziert werden, werden sie nicht mehr sofort in die alte Generation hochgestuft, wodurch die Häufigkeit großer GC-Durchläufe stark sinkt.
- Die neue Tuning-Variable
REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO, die die Anzahl ungeschützter Objekte steuert, welche große GC-Durchläufe auslösen, wurde eingeführt. Der Standardwert liegt bei 0,01 (1 %), wodurch die Häufigkeit großer GC-Durchläufe deutlich sinkt.
- Für viele Core-Typen, bei denen Write Barriers fehlten, wurde Unterstützung implementiert. Dadurch sinken die Zeiten kleiner GC-Durchläufe und die Häufigkeit großer GC-Durchläufe erheblich.
- Die meisten Core-Klassen verwenden nun Variable Width Allocation. Das beschleunigt Allokation und Freigabe dieser Klassen, reduziert den Speicherverbrauch und verringert die Heap-Fragmentierung.
- Schwache Referenzen werden nun vom Garbage Collector unterstützt.
Weitere bemerkenswerte Änderungen
- IRB erhielt zahlreiche Verbesserungen, darunter eine fortgeschrittene irb:rdbg-Integration, Pager-Unterstützung für die Befehle ls, show_source und show_cmds, genauere und nützlichere Informationen durch ls und show_source sowie experimentelle Autovervollständigung mithilfe von Typanalyse.
- IRB wurde außerdem umfassend refaktoriert, um künftige Verbesserungen zu erleichtern, und erhielt Dutzende Bugfixes.
Kompatibilitätsprobleme
- Der Aufruf von
it in Blöcken ohne Argumente wird nicht mehr verwendet; in Ruby 3.4 wird er auf den ersten Block-Parameter verweisen.
- Nicht verwendete Umgebungsvariablen wurden entfernt.
Updates der Standardbibliothek
- RubyGems und Bundler zeigen eine Warnung an, wenn Nutzer die folgenden gems anfordern, ohne sie zu Gemfile oder gemspec hinzuzufügen. Der Grund ist, dass diese gems in künftigen Ruby-Versionen zu gebündelten gems werden sollen.
- Die folgenden Standard-gems wurden hinzugefügt oder aktualisiert: prism 0.19.0, RubyGems 3.5.3, abbrev 0.1.2 und viele weitere.
- Die folgenden gebündelten gems wurden aus Standard-gems hochgestuft oder aktualisiert: racc 1.7.3, minitest 5.20.0 und viele weitere.
Meinung von GN⁺
- Einführung des Prism-Parsers: Eines der wichtigsten Merkmale von Ruby 3.3.0 ist die Einführung des neuen Prism-Parsers. Er dürfte Ruby-Entwicklern erheblich helfen, indem er Ruby-Code effizienter parst und einen fehlertoleranten sowie leicht wartbaren Parser bereitstellt.
- Leistungsverbesserungen bei YJIT: Die wesentlichen Performance-Verbesserungen von YJIT werden die Ausführungsgeschwindigkeit von Ruby-Anwendungen deutlich erhöhen. Besonders die Reduzierung des Speicherverbrauchs und die GC-Optimierungen dürften sich positiv auf Performance und Stabilität großer Ruby-Anwendungen auswirken.
- M:N-Thread-Scheduler: Die Einführung des M:N-Thread-Schedulers hat das Potenzial, die Performance multithreaded Ruby-Anwendungen zu verbessern. Er dürfte den Aufwand für Thread-Verwaltung senken und effizientere Parallelverarbeitung ermöglichen.
1 Kommentare
Hacker-News-Kommentare
Mit dem Erscheinen von Ruby 3.3 legt Ruby, eine Sprache, die viel Wert auf das Glück der Entwickler legt, ihr früheres langsames Image ab und glänzt nun mit hoher Geschwindigkeit.
Ruby 3.3 ist das wichtigste und funktionsreichste Release der letzten zehn Jahre, und es ist überraschend, dass es noch vor Python ein JIT veröffentlicht hat.
Hinweis, dass Ruby 3.3 auf Heroku verfügbar ist.
Jedes Jahr zu Weihnachten veröffentlicht die Ruby-Sprache ein neues Release.
Frage, ob es sich lohnt, Ruby zu lernen, wenn man Python und NodeJS bereits kennt. Ruby wirkt attraktiv, aber auch schwierig.
Die Namensauflösung wie bei
Socket.getaddrinfokann blockieren. Jedes Mal, wenn eine Namensauflösung nötig ist, wird ein Worker-pthread erzeugt undgetaddrinfo(3)ausgeführt.Prism ist interessant. Frage, ob es Beispiele dafür gibt, Prism als Ruby-Code-Analysetool zu verwenden.
Die Umgebungsvariable
RUBY_MAX_CPU=nlegt die maximale Anzahl nativer Threads fest. Der Standardwert ist 8.Suche nach guten Beispielen für die Verwendung von Prism. Enttäuschung darüber, dass auf der Release-Seite außer den „bemerkenswerten APIs“ nicht viel zu finden ist.
Erwähnung, dass dies das perfekte Weihnachtsgeschenk sei.