1 Punkte von GN⁺ 5 일 전 | 1 Kommentare | Auf WhatsApp teilen
  • Ein AOT-Compiler, der Ruby-Quellcode nach programweiter Typherleitung in C-Code umwandelt und eigenständige native Binärdateien erzeugt
  • Direkt vom Ruby-Schöpfer matz entwickelt; das Compiler-Backend selbst ist in Ruby geschrieben und nutzt eine Self-Hosting-Architektur, in der es sich selbst kompiliert
  • Gegenüber miniruby (Ruby 4.1.0dev) etwa 11,6-mal schneller; Conway's Game of Life ist 86,7-mal, ackermann 74,8-mal und mandelbrot 58,1-mal schneller
  • Die Compile-Pipeline wandelt Ruby mit einem Prism-basierten Parser zunächst in AST-Text um; anschließend übernimmt das Self-Hosting-Backend Typherleitung und C-Code-Erzeugung, bevor mit einem Standard-C-Compiler ein Standalone-Binary erstellt wird
  • Unterstützt ein breites Spektrum an Ruby-Funktionen wie Klassen, Vererbung, Blöcke, Exception-Handling, Fiber, die eingebaute NFA-Regexp-Engine, automatisch hochgestufte Bigints und Pattern Matching
  • Kleine Klassen mit bis zu 8 skalaren Feldern werden als Value Types automatisch auf dem Stack alloziert, wodurch der GC-Overhead vollständig entfällt (1 Million Allokationen 85 ms → 2 ms)
  • String-Chaining mit a + b + c + d wird mit einem einzigen malloc flach zusammengeführt; split innerhalb von Schleifen verwendet sp_StrArray wieder, um unnötige Allokationen zu vermeiden
  • Zahlreiche Compile-Time-Optimierungen wie Hoisting schleifeninvarianter Längen, Constant Propagation, automatisches Inlining von Methoden mit bis zu 3 Anweisungen und frühzeitiger Abbruch iterativer Inferenz (~14 % kürzere Bootstrap-Zeit)
  • Die erzeugten Binärdateien haben keine Runtime-Abhängigkeiten und lassen sich nur mit libc + libm ausführen
  • eval, Metaprogrammierung, Thread und Mutex werden nicht unterstützt (nur Fiber wird unterstützt)
  • MIT-Lizenz

1 Kommentare

 
GN⁺ 5 일 전
Hacker-News-Kommentare
  • Wenn es von Matz stammt, vertraut man darauf, dass er auch die Grenzen der Ruby-Semantik gut kennt
    Meine Masterarbeit war ebenfalls ein AOT-JS-Compiler; er lief zwar, aber die Einschränkungen bei den Eingabedaten waren so groß, dass ich das Projekt schließlich aufgegeben habe
    Damals waren JS-Entwickler nicht besonders daran gewöhnt, solche Einschränkungen selbst diszipliniert einzuhalten, und grundsätzlich nicht vorhersagbare Eingaben wie bei JSON.parse waren ein Hindernis
    Heute könnte es dank TypeScript deutlich realistischer sein als damals
    Schon beim allgemeinen Lambda-Kalkül sind die Grenzen der Typinferenz klar, und in Arbeiten von Matt Might oder bei Shed-skin Python treten ähnliche Beschränkungen zutage
    Ich frage mich, wie verbreitet eval, send, method_missing und define_method in realem Ruby-Code tatsächlich sind, und auch, wie man typfreies Parsing, etwa bei JSON-Eingaben, normalerweise handhabt

    • Dieses Design wirkt ziemlich pragmatisch
      Ruby zu parsen ist fast schwieriger als die eigentliche Übersetzung, deshalb wird Prism verwendet, und als Ergebnis wird C erzeugt
      Die grundlegende Ruby-Semantik selbst ist gar nicht so schwer umzusetzen
      Ich selbst hänge dagegen an einem alten self-hosting AOT-Compiler, der in purem Ruby geschrieben ist, und habe mir absichtlich den deutlich schwereren Weg ausgesucht, weil ich unbedingt einen eigenen Parser verwenden wollte
      Früh habe ich gelernt, dass man die ersten 80 % grob zusammenbauen kann und dann schon ein beträchtlicher Teil von Ruby-Code läuft; die wirklich schwierigen „zweiten 80 %“ stecken in den Dingen, die Matz in diesem Projekt und in mruby weggelassen hat, etwa Encodings oder allerlei Randfunktionen
      Ehrlich gesagt hat Ruby einige Features, die ich in echtem Code noch nie gesehen habe, daher fände ich es bei manchen nicht einmal seltsam, wenn sie deprecated würden
      send, method_missing und define_method sind sehr verbreitet
      Die Einschränkungen ähneln denen von mruby, und selbst unter solchen Einschränkungen gibt es Einsatzmöglichkeiten
      send, method_missing und define_method zu unterstützen, ist vergleichsweise einfach
      eval() zu unterstützen ist dagegen extrem schmerzhaft
      Ein großer Teil von eval() in Ruby lässt sich aber statisch auf die Block-Variante von instance_eval zurückführen, und in solchen Fällen wird AOT-Kompilierung recht einfach
      Wenn man zum Beispiel den String, der in eval() hineingeht, statisch kennt oder zerlegen kann, hat man viel Spielraum
      Tatsächlich ist viel eval()-Nutzung unnötig oder eher ein Umgehen einfacher Introspection, sodass man das mit statischer Analyse abfangen kann
      In meinem Compiler würde ich dort ansetzen, wenn das zum Engpass wird
    • Von solchen Features braucht man ziemlich viele, um Rails-artige Magie zu bauen
      Auch typfreie JSON-Verarbeitung dürfte wahrscheinlich auf solche Mechanismen setzen
      Wenn man das entfernt, bleibt eine kleine, gut lesbare Sprache übrig, die nicht so stark typisiert ist wie Crystal, sich aber auch nicht so stark auf Metaprogrammierung stützt wie offizielles Ruby
      Das Potenzial wirkt daher ziemlich groß, aber beurteilen lässt es sich erst mit der Zeit
    • Wenn Ruby nach Objective-C kompiliert würde, könnte es vielleicht alle Ruby-Features unterstützen und trotzdem schneller sein als interpretiertes Ruby
    • Ich gehöre eher zu den Leuten, die eval häufig verwenden
      Ich könnte auch ohne auskommen, aber für mich ist das einfach ergonomischer
    • Aus meiner Erfahrung sind vor allem eval, exec, define_method sowie Muster wie das Erzeugen neuer Klassen mit Class.new und Struct.new interessant
      Der Großteil dieser Nutzung konzentriert sich auf den Boot-Zeitpunkt der App oder auf die Phase, in der Dateien per require geladen werden; in gewisser Hinsicht ist das ohnehin schon fast ein Kompilierungsschritt
  • Das ist etwas, das Matz gerade auf der RubyKaigi 2026 vorgestellt hat
    Es ist experimentell, wurde aber mit Hilfe von Claude in etwa einem Monat gebaut, und auch die Live-Demo hat funktioniert
    Der Name stammt von Matz’ neuer Katze; deren Name wiederum kommt von der Katze aus Card Captor Sakura, wo sie wiederum ein Gegenstück zu einer Figur namens Ruby bildet

    • Viele reden darüber, dass AI Programme von Anfang bis Ende komplett baut, aber ich halte es für realistischer, dass sie einen 10x programmer zu einem 100x programmer macht
      Bei jemandem wie Matz könnte das sogar eher von 100x auf 500x gehen
    • In meinem Kopf ist Spinel aktuell mit Steven Universe verknüpft, daher habe ich das Spinel/Ruby-(Moon)-Wortspiel überhaupt nicht verstanden; jetzt, da ich es weiß, ist mein Tag schöner geworden
    • Ich dachte natürlich, es gehe um das Mineral Spinel :)
      https://en.wikipedia.org/wiki/Spinel
    • Danke
      Das Video scheint noch nicht live zu sein; offenbar werden sie hier nach und nach hochgeladen
      https://www.youtube.com/@rubykaigi4884/videos
    • Die Geschichte zur Herkunft des Katzennamens wirkt ziemlich verdächtig, wenn man an das Ruby-Central-Drama und die Beziehung zu den Gründern von Spinel.coop denkt
      Auch der Projektname wirkt, als wäre er emotional gewählt worden
  • Das ist zweifellos sehr beeindruckend, aber ohne AI agent scheint es kaum wartbar
    spinel_codegen.rb hat 21.000 Zeilen, und manche Methoden sind bis zu 15 Ebenen tief verschachtelt
    Compiler-Code ist von Natur aus selten schön, aber selbst nach diesem Maßstab wirkt das für Menschen extrem schwer wartbar

    • Compiler-Code kann mit genug Zeit durchaus schön gemacht werden
      Compiler haben klare Grenzen zwischen Subsystemen und eindeutige Übergaben zwischen den Phasen; gerade deshalb gehören sie eher zu den Dingen, die sich am leichtesten modular aufbauen lassen
      Das Problem ist meist, dass man erst einmal etwas Lauffähiges baut und dann keine Zeit mehr zum Refactoring bleibt; so wächst die Unordnung immer weiter
    • spinel_codegen.rb ist fast schon eldritch horror
      Mit Claude produziere ich auch ständig solchen Spaghetti-Code und habe mich schon gefragt, ob ich etwas falsch mache
      Aber wenn ich bei einem wirklich interessanten Projekt eines Programmierers, den ich zur absoluten Spitzenklasse zähle, ebenfalls an vielen Stellen ziemlich schlechte Codequalität sehe, dann bin ich wohl nicht allein
      Zum Beispiel ist infer_comparison_type() nicht das allerschlimmste Beispiel und auch nicht unlesbar, aber es gäbe eine viel einfachere und klarere Umsetzung, zu der Claude einfach nicht findet
      Wenn man die Vergleichsoperatoren in einem Set sammelt und mit include? prüft, wäre es kürzer, schneller, lesbarer und leichter wartbar
      Claude driftet aber immer zu if-return-Ketten ab und wirkt fast so, als wäre ihm sogar if-else fremd
      Meine eigene Claude-Codebasis ist voll von solchen Mustern; jetzt weiß ich immerhin, dass das nicht nur bei mir so ist
      Andere Dateien sind dagegen viel besser, und insbesondere das lib-Verzeichnis scheint dem ext-Verzeichnis des Haupt-Ruby-Repos zu entsprechen und wirkt qualitativ ordentlich
      Auch die API trägt klar den Einfluss von MRI Ruby; selbst wenn die Implementierung recht anders ist, scheint Matz Claude dahin gelenkt zu haben, Teile der Original-API nachzuahmen, wodurch die Ausgabe aufgeräumter wirkt
      [1] https://github.com/matz/spinel/blob/98d1179670e4d6486bbd1547...
    • In diesem Stadium halte ich es nicht für so wichtig, ob Menschen das von Hand warten können
      Solange Tests und Benchmarks durchlaufen, bin ich erst einmal zufrieden
      Ich frage mich aber, ob riesige Dateien auch für AI gut handhabbar sind
      Ich versuche, Dateien auf unter 300 Zeilen zu begrenzen, und glaube, dass Code, den Menschen leicht verstehen, auch für coding agents leichter ist
  • Die Einschränkungen sollen diese sein
    No eval: eval, instance_eval, class_eval
    No metaprogramming: send, method_missing, define_method (dynamisch)
    No threads: Thread, Mutex (Fiber wird unterstützt)
    No encoding: Annahme von UTF-8/ASCII
    No general lambda calculus: tief geschachtelte -> x { } mit []-Aufrufen
    Die Annahme von UTF-8/ASCII ist für mich persönlich keine große Einschränkung, aber der Rest dürfte in ziemlich vielen Programmen real relevant sein
    Und um das wieder einzubauen, scheint erheblicher Aufwand nötig zu sein

    • Dadurch verschwindet ein großer Teil der Ruby-Magie
  • Ich nutze Ruby schon lange, und als jemand, der alle aufgelisteten Features tatsächlich verwendet hat, merke ich: Genau so eine Version von einfachem Ruby ist letztlich das, was ich mir nach all der Entwicklung eher wünsche
    Sie ist einfacher und leichter zu verstehen, ohne dass Rubys eigener ästhetischer Charakter verloren geht
    Durch LLMs ist die Produktivität bei der Codeerzeugung inzwischen so hoch, dass man Metaprogrammierung nicht mehr so sehr braucht wie früher, um Boilerplate für die Produktivität von Entwicklern zu reduzieren
    Schließlich schreiben Entwickler selbst insgesamt einen kleineren Anteil des Codes

    • Wenn man eigentlich nur die Ruby-Ästhetik will, könnte Crystal ebenfalls sehr gut passen
      Die Syntax ist ähnlich, und es gibt ein statisches Typsystem, was zu effizienter kompiliertem Code führt
    • Dass eval fehlt, finde ich eher positiv, aber dass sogar threads und mutexes fehlen, ist schade
      Das Fehlen von define_method kann ich angesichts seines Einsatzzwecks noch nachvollziehen
      Aber send und method_missing sind in bestehender Bibliothekslandschaft verbreitet, und ihre Implementierung scheint auch nicht völlig unmöglich, etwa indem man Lookup-Tabellen im Speicher zur Compile-Zeit aufbaut
      Daher weiß ich nicht, ob sie absichtlich ausgeschlossen wurden oder ob man einfach noch nicht so weit ist
      Ich hoffe auf Letzteres, aber zumindest vorerst wäre produktiver Einsatz wegen der Kompatibilität wohl schwierig
    • Der Vorteil von Metaprogrammierung bestand ursprünglich nicht darin, weniger Code zu schreiben
      Sondern darin, weniger Code lesen zu müssen
  • Das ist wirklich cool, und ich warte schon lange auf einen AOT-Compiler für Ruby
    Schade ist nur, dass es kein Fallback für eval oder Metaprogrammierung gibt; vermutlich hat man sich bewusst auf ein kleines, performantes Subset konzentriert
    Es wäre schön, wenn mit diesem AOT-Compiler gebaute Gems gut mit MRI interagieren würden
    Für das Paketieren oder Bundeln von Standard-Ruby und Gems braucht man weiterhin Dinge wie tebako, kompo oder ocran; früher gab es auch Projekte wie ruby-packer, traveling ruby oder jruby warbler
    Es ist gut, eine weitere Option zu haben, aber ich hoffe auf eine endgültige Lösung mit besserer Developer-UX

    • Stimmt, ich musste vor Kurzem auch warbler forken
      Es war einfach viel zu lange nicht aktualisiert worden
  • Ich frage mich, warum no threads gilt
    Rubys Scheduler und die zugrunde liegende pthread-Implementierung müssten doch auch im C-Bereich gut funktionieren; vielleicht zielt man auf zero dependency ab
    Falls nicht geplant ist, später eine optionale Erweiterung einzubauen, und Threads nicht nur vorläufig fehlen, wirkt diese Entscheidung etwas seltsam

    • Ich habe noch keinen Hinweis gesehen, dass man sich bewusst gegen Unterstützung entschieden hätte
      Wahrscheinlich ist man schlicht noch nicht so weit
      Multithreading sauber zu bauen, ist ohnehin sehr schwierig
  • Überraschend, dass das in etwas mehr als einem Monat gebaut wurde
    Man kann über AI sagen, was man will, aber in den Händen eines fähigen Entwicklers sorgt sie für enorme Geschwindigkeitsgewinne

    • Die ganze Branche startet mit agent harness, SOUL.md, Berechtigungseinstellungen, skills, MCPs, hooks und env
      Bei Matz wirkt es, als würden einfach gem env|info und find reichen
  • Da das von Matz stammt, frage ich mich, wie realistisch es ist, dass es künftig Teil von Ruby core wird
    Und falls das passiert, wie sehr es Crystal bedrohen könnte

    • Crystal hat ein explizites statisches Typsystem und ist auf Sprachebene für AOT-Kompilierung optimiert
      Diese Eigenschaften sind für das Kompilieren und Warten großer Programme praktisch unverzichtbar
      Hier geht es dagegen um ein eingeschränktes Ruby-Subset, sodass die meisten populären Ruby-Gems unverändert nicht laufen werden
      Als Sprachsubset, das auf C-Kompilierung abzielt, erinnert es eher an PreScheme
      In diesem Stadium sehe ich die beiden nicht in direkter Konkurrenz im selben Bereich
      Vollständiges Ruby wird mit hoher Wahrscheinlichkeit JIT brauchen
      [1]: https://prescheme.org/
    • Aus einer anderen Perspektive betrachtet scheinen wir ohnehin auf den Punkt zuzusteuern, an dem LLMs für jede beliebige Sprache, die wir wollen, einfach eine formale Spezifikation ausspucken
      Das wäre die Revanche von Werkzeugen wie Rational Unified Process oder Enterprise Architect
      Der Unterschied bestünde nur darin, dass statt UML-Diagrammen Markdown-Dateien geliefert würden
  • Dafür dürfte es im Bereich Infrastructure Tools nützlich sein
    Man könnte sich zum Beispiel einen statisch kompilierten bundler vorstellen, der in Ruby geschrieben ist, aber zugleich die Rolle eines Ruby-Installationstools wie RVM übernimmt
    Bestehende Ruby-Buildpacks sind in Ruby geschrieben, müssen aber per bash gebootstrapped werden, was nervig ist und Edge Cases erzeugt
    CNB wurde geschrieben, um genau dieses Problem zu vermeiden, und die Idee, ein abhängigkeitsfreies Single-Binary auszuliefern, ist wirklich stark