diff --git a/hugo.toml b/hugo.toml index a4c0927..2bd4280 100644 --- a/hugo.toml +++ b/hugo.toml @@ -40,4 +40,4 @@ disableKinds = ["taxonomy", "term", "RSS", "sitemap", "404"] [[params.phases]] name = "Phase 8 — Conformance & performance" status = "active" - blurb = "Since July the work has been **runtime** conformance, measured against [ruby/spec](https://github.com/ruby/spec)'s `language/` + `core/` suites: **21,982 examples pass** (measured 2026-09-23 on `main`, `d498ddc`, four consecutive runs all identical), gated in CI by a **shrink-only floor of 21,970** — the floor is the level CI refuses to fall below, *not* the score. It sits just under the measured total by design: a margin wide enough for the run-to-run jitter, narrow enough that losing a whole spec file still trips it. It has climbed from 6,000 when the ratchet landed on 2026-08-03, across 27 waves. Two of the recent jumps were fixes to the **measurement** rather than the interpreter — the mspec shim, and a parser upgrade that made 13 previously-unparseable spec files visible — and those are gains in what the measurement can *see*, not in what the VM can *do*. Earlier, a conformance campaign brought the pure-Go **front-end** to ~**100% of all valid Ruby** parsed across Rails + Puppet (99.96% of files; 2 misses are syntax-error fixtures MRI rejects) and **parse + compile** of **Rails 99.82% / Puppet 100%**, with **0 over-permissive** — and RuboCop 99.7%, Sinatra/Jekyll/Thor/Kramdown/dry-struct 100%, RSpec DSL 10/10. On the runtime side, that road is now underway: **the real `puppet apply` CLI runs end-to-end** on a pure-Go CGO=0 `rbgo` — `require \"puppet\"` boots Puppet 8.11.0 (all pure-Ruby gem deps on the path), and the genuine `Puppet::Util::CommandLine` → `Puppet::Application::Apply` path (real `OptionParser`, all types + providers, settings catalog applied to disk, then the user catalog via the transaction/RAL) converges `notify`, `file` and `exec` resources (the `exec` provider runs commands via pure-Go process execution with `onlyif`/`unless`/`creates`/`path` guards), emits real Puppet output (`Notice: hi from rbgo cli`) and exits 0 with the YAML run report round-tripped. Broader resource **providers** are the active next milestone (`user`/`group` provider-ready; `package`/`service` need a host package manager/systemd + root). rbgo is also increasingly composed from the standalone pure-Go **go-ruby-* family** — **178 modules across 176 orgs are now bound into rbgo as native modules** (parser, regexp, erb, marshal, yaml, format, optparse, strscan, json, date, … a selection is on the landing page), each its own 100%-coverage 6-arch org. (`Set` is not among them: it is implemented in-tree, over rbgo's own Hash.) Every one of the six arches is validated on **real hardware**, not just qemu — amd64/arm64 natively, riscv64/ppc64le/loong64 on the GCC Compile Farm (RVV, POWER8E/POWER9, LoongArch), s390x on the IBM LinuxONE Community Cloud — which is what makes the SIMD-accelerated modules and perf numbers measured rather than estimated. On performance, a 6-runtime suite (rbgo/rbgo+AOT vs MRI/YJIT/JRuby/TruffleRuby): **rbgo+AOT beats MRI+YJIT 18–24× on loop/fib**; plus inline method caches, small-integer interning (a small-int loop drops from ~245k allocations to 1) and capture-tracked frame recycling (~14% faster on fib)." + blurb = "Since July the work has been **runtime** conformance, measured against [ruby/spec](https://github.com/ruby/spec)'s `language/` + `core/` suites: **22,488 examples pass** (measured 2026-09-26 on `main`, `3e8e3cc`; three independent runs, identical), gated in CI by a **shrink-only floor of 22,475** — the floor is the level CI refuses to fall below, *not* the score. It has climbed from 6,000 when the ratchet landed on 2026-08-03. **Only `language/` and `core/` are swept** — ruby/spec's `library/`, `optional/` and `security/` trees are not run at all, so this is not a percentage of Ruby. Two of the recent jumps were fixes to the **measurement** rather than the interpreter — the mspec shim, and a parser upgrade that made 13 previously-unparseable spec files visible — and those are gains in what the measurement can *see*, not in what the VM can *do*. Earlier, a conformance campaign brought the pure-Go **front-end** to ~**100% of all valid Ruby** parsed across Rails + Puppet (99.96% of files; 2 misses are syntax-error fixtures MRI rejects) and **parse + compile** of **Rails 99.82% / Puppet 100%**, with **0 over-permissive** — and RuboCop 99.7%, Sinatra/Jekyll/Thor/Kramdown/dry-struct 100%, RSpec DSL 10/10. On the runtime side, that road is now underway: **the real `puppet apply` CLI runs end-to-end** on a pure-Go CGO=0 `rbgo` — `require \"puppet\"` boots Puppet 8.11.0 (all pure-Ruby gem deps on the path), and the genuine `Puppet::Util::CommandLine` → `Puppet::Application::Apply` path (real `OptionParser`, all types + providers, settings catalog applied to disk, then the user catalog via the transaction/RAL) converges `notify`, `file` and `exec` resources (the `exec` provider runs commands via pure-Go process execution with `onlyif`/`unless`/`creates`/`path` guards), emits real Puppet output (`Notice: hi from rbgo cli`) and exits 0 with the YAML run report round-tripped. Broader resource **providers** are the active next milestone (`user`/`group` provider-ready; `package`/`service` need a host package manager/systemd + root). rbgo is also increasingly composed from the standalone pure-Go **go-ruby-* family** — **178 modules across 176 orgs are now bound into rbgo as native modules** (parser, regexp, erb, marshal, yaml, format, optparse, strscan, json, date, … a selection is on the landing page), each its own 100%-coverage 6-arch org. (`Set` is not among them: it is implemented in-tree, over rbgo's own Hash.) Of the six arches, amd64 and arm64 are tested on every pull request; riscv64, loong64, ppc64le and s390x are validated on a **nightly schedule** — under qemu, and natively on the GCC Compile Farm plus an IBM LinuxONE s390x host — not on the pull-request path. On performance, a 6-runtime suite (rbgo/rbgo+AOT vs MRI/YJIT/JRuby/TruffleRuby): **rbgo+AOT beats MRI+YJIT on integer-bound kernels — measured here at 8.7× on the tight-loop benchmark and 2.0× on recursive `fib(35)`** (darwin/arm64, whole-process wall time); plus inline method caches, small-integer interning (a small-int loop drops from ~245k allocations to 1) and capture-tracked frame recycling (~14% faster on fib)." diff --git a/layouts/_default/list.html b/layouts/_default/list.html index c5473bc..5d039f4 100644 --- a/layouts/_default/list.html +++ b/layouts/_default/list.html @@ -54,9 +54,9 @@

A · Per-module benchmarks

B · Runtime micro-benchmarks

-

rbgo's ahead-of-time backend compiles hot methods to unboxed Go kernels. Against MRI with YJIT enabled, on tight loop / fib kernels:

+

rbgo's ahead-of-time backend compiles integer-bound methods to unboxed Go kernels. Measured on this project's own micro-benchmarks (bench/loop.rb, bench/fib.rb) on darwin/arm64, best-of-N whole-process wall time, against MRI 4.0.5 with YJIT enabled:

-
18–24×

AOT vs MRI+YJIT

rbgo+AOT beats MRI with YJIT enabled by 18–24× on loop / fib — unboxed int64 kernels, no boxing on the hot path.

+
8.7×

AOT vs MRI+YJIT

On the tight-loop benchmark, rbgo+AOT ran 8.7× faster than MRI+YJIT (43 ms vs 373 ms). On recursive fib(35) the margin is 2.0× (56 ms vs 110 ms) — recursion keeps the call overhead the loop case removes. Only methods the AOT backend can lower benefit at all.

245k → 1

Small-integer interning

Interned small integers drop a small-int loop from ~245,000 allocations to 1 — the hot integers are shared, immutable singletons.

~14%

Frame recycling

Capture-tracked frame recycling is ≈14% faster on fib; frames are reused unless a closure captures them. Plus inline method caches on every call site.

@@ -68,11 +68,12 @@

C · Application conformance & performance

Runtime conformance — ruby/spec

-

21,982 examples pass in ruby/spec's language/ + core/ suites, measured 2026-09-23 on main (d498ddc), four consecutive runs all identical.

+

22,488 examples pass in ruby/spec's language/ + core/ suites, measured 2026-09-26 on main (3e8e3cc). Three independent runs — two through the ratchet at different timeouts, one through a separate sweep — reported the same total.

    -
  • CI gates on a shrink-only floor of 21,970, raised there after wave 27. The floor is the level a run may not fall below — it is not the score. It sits just under the measured total on purpose: a margin of 12, wide enough to absorb the run-to-run jitter of #615 and no wider, because a floor slack enough to hide the loss of a whole spec file would defeat what the ratchet exists to catch.
  • -
  • Of the examples that ran, 91.4% pass (21,983 of 24,044); 473 more are skipped and 15 of 2,206 spec files produce no result.
  • -
  • The floor has climbed from 6,000 on 2026-08-03 to 21,970 across 27 waves. Two recent jumps were fixes to the measurement — the mspec shim, and a parser upgrade that made 13 previously-unparseable spec files visible — which are gains in what the measurement can see, not in what the VM can do.
  • +
  • CI gates on a shrink-only floor of 22,475. The floor is the level a run may not fall below — it is not the score: it is what CI guarantees, while the measured total is what one run did.
  • +
  • Only language/ and core/ are swept. ruby/spec's library/, optional/ and security/ trees are not run at all, so this figure is not a percentage of Ruby, and most of the standard library sits outside it.
  • +
  • Of the examples that ran, 93.3% pass (22,488 of 24,116); 477 more are skipped because our shim stubs the matcher they need, and 10 of 2,206 spec files produce no result. Skips and unreadable files sit outside the ratio entirely.
  • +
  • The floor has climbed from 6,000 on 2026-08-03 to 22,475. Two earlier jumps were fixes to the measurement — the mspec shim, and a parser upgrade that made 13 previously-unparseable spec files visible — which are gains in what the measurement can see, not in what the VM can do.
diff --git a/layouts/index.html b/layouts/index.html index 2bfb272..112b07d 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -18,13 +18,13 @@
-

A complete Ruby,
written in Go.

+

A Ruby you can embed,
written in Go.

go-embedded-ruby (rbgo) is a from-scratch Ruby — lexer, parser, compiler, bytecode VM and an AOT backend — that embeds in any Go program and cross-compiles to a single CGO-free binary. Built in the open as a digital common (commun numérique), free to use, study, modify and share under the BSD-3-Clause licence.

-
21,982 ruby/spec examples passing~100% of real Ruby parsed6 64-bit arches
+
22,488 ruby/spec language/+core/ examples passing~100% of real Ruby parsed6 64-bit arches
rbgo — zsh
@@ -32,9 +32,11 @@

A complete Ruby,
written in Go.< $ rbgo -e 'puts (1..20).reduce(:*)' 2432902008176640000 -# AOT-compile the hot path to native Go +# AOT-compile the hot path and drop the front-end $ rbgo build --closed app.rb -o app -→ app (CGO=0, 1 file, runs anywhere) +rbgo build: app (1 method(s) AOT-compiled: Object#fib) +rbgo build: closed-world — front-end dropped +rbgo build: binary size 172.5 MiB # the real Puppet CLI, end to end $ rbgo run puppet_apply.rb @@ -54,10 +56,10 @@

A complete Ruby,
written in Go.<

Everything a Ruby runtime gives you — as a Go library.

No FFI, no libruby, no system Ruby. Drop a genuine, monkey-patchable Ruby object model into your Go binary and ship it anywhere.

-
🧩

Truly embeddable

A real object model — classes, mixins, method_missing, metaprogramming, Fiber — callable straight from Go.

+
🧩

Embeddable

A real object model — classes, mixins, method_missing, metaprogramming, Fiber. The Go API is deliberately small today: ruby.Run(src, out) runs a program on a fresh VM and writes its output to one writer. Calling a Ruby method from Go is not exposed yet.

📦

One static binary

CGO=0 by default. Cross-compiles to amd64, arm64, riscv64, loong64, ppc64le and s390x.

-
⚡

AOT that flies

Ahead-of-time method compilation to unboxed Go kernels — 18–24× over MRI+YJIT on loop/fib.

-
✅

Conformance-first

21,982 ruby/spec language/ + core/ examples pass, gated in CI by a shrink-only floor of 21,970 — the floor is what CI refuses to fall below, not the score. It sits just under the measured total by design: a margin wide enough for run-to-run jitter, narrow enough that losing a whole spec file still trips it. Front-end: ~100% of valid Ruby parsed across Rails + Puppet (2026-06).

+
⚡

AOT for integer kernels

Ahead-of-time compilation of integer-bound methods to unboxed Go. Measured on this project’s own micro-benchmarks (darwin/arm64, whole-process wall time): 8.7× MRI+YJIT on the tight-loop case, 2.0× on recursive fib(35). Only methods the compiler can lower benefit.

+
✅

Conformance, measured

22,488 ruby/spec language/ + core/ examples pass, gated in CI by a shrink-only floor of 22,475 — the floor is what CI refuses to fall below, not the score. Only language/ and core/ are swept: library/, optional/ and security/ are not run, so this is not a percentage of Ruby. Front-end: ~100% of valid Ruby parsed across Rails + Puppet (2026-06).

🔒

Closed-world builds

rbgo build --closed bakes the program to bytecode and drops the front-end for a smaller binary.

🌿

A real ecosystem

Built from the go-ruby-* family — each a 100%-coverage, 6-arch stdlib module, bound natively.

@@ -65,8 +67,8 @@

Everything a Ruby runtime gives you — as a Go library.

-
18–24×
rbgo+AOT vs MRI+YJIT (loop/fib)
-
21,982
ruby/spec examples passing · CI floor 21,970
+
8.7×
rbgo+AOT vs MRI+YJIT, tight-loop benchmark (2.0× on fib(35))
+
22,488
ruby/spec language/+core/ examples passing · CI floor 22,475
178
go-ruby-* modules bound into rbgo
CGO=0
across 6 arches · 3 OSes