The Ruby Tumbler project is going surprisingly well. I've gotten further than expected, and learned some fascinating things about dynamic languages.
It's also going slower than expected.
I've had to be more methodical with these experiments and think hard about what an efficient native Ruby compiler should look like.
Preserving dynamic behavior
Because the problem isn't simply translating Ruby into machine code.
Ruby is extraordinarily dynamic. Methods can be replaced at runtime. Objects can change behavior. Closures can outlive their creators. And programmers expect it all to keep working.
The trick is preserving those behaviors without paying for all the machinery all the time.
That's led to one of my favorite experiments: Rust-inspired memory management, without asking Ruby devs to think about ownership scenarios.
The compiler can prove when a block won't escape, borrow its captured storage, and avoid allocating a heap closure. At runtime, it checks the method that's actually going to execute.
If that implementation isn't proven safe, it falls back to ordinary heap-managed behavior.
No ownership annotations, lifetime declarations, or rewriting your app.
In controlled tests, this process eliminated two allocations per call, and all garbage collections in the timed batches. I'm really pleased with how this is coming together.
I'm still working toward CRuby compatibility. Some dynamic features need more work, and the experimental native-object backend doesn't fully boot Rails yet. I'm testing with half a dozen randomly selected Rails repos of different sizes and complexity, because I'd rather find the awkward edge cases now.
My goal is to avoid forcing devs into major refactoring just to benefit from native compilation.
Meanwhile, under the hood, the whole machine has changed.
Entire categories of runtime work have been consolidated, specialized, or eliminated.
And I'm realizing that the important question isn't always how to make individual Ruby operations faster, so much as how to avoid performing unnecessary operations in the first place.
I'm prioritizing runtime performance and memory management over compilation speed for the time being.
What the benchmarks show
How much faster is it?
My internal benchmarks still show around 3× CRuby on the workloads I've been exploring.
That's far from definitive. Some operations improve much more. Others less. I can't yet promise a speedup for any particular class of Rails application in every situation... yet.
But something interesting is happening.
What started as a collection of experiments now has a coherent philosophy around dynamic dispatch, memory ownership, and preserving language semantics without unnecessary overhead.
It's beginning to look less like a pile of experiments and more like an actual coherent product.
Depending on my work schedule, I may need another couple of weeks before I'm ready to publish my repo.
I'm excited about that.
Can't wait to share this.
I love you all.