Looks like they aren't intending to use Cranelift. Which makes sense for an initial port that already has its own codegen. Personally found Cranelift a joy to use. Would be nice if Cranelift had a WASM backend to could compile a JIT to WASM to JIT WASM in WASM (this cyclic feature would be useful for creating JITs for dynamic languages in WASM rather than compiling the C interpreters to WASM)
It's unfortunate, but cranelift might still be considered too unstable for "primetime": my understanding as an outsider is that they're looking to stabilize YJIT sooner rather than later, to prevent it from being a perpetually unstable/experimental feature like MJIT ended up being.
(Also, the last time I checked, cranelift emitted extremely suboptimal code. That's not their fault given the design constraints (it's a new project and they're aiming to beat LLVM's SelectionDAG in codegen performance), but it's probably not ideal given Ruby's long term performance goals. I'm also not sure how easy it would be to retrofit the LBBV[1] architecture of YJIT onto cranelift.)
> Rust as a programming language community has a great deal of enthusiasm behind it. This could translate to more enthusiasm for YJIT and for Ruby as well.
It's refreshing to see a project explicitly call this out as a benefit for adopting Rust. Love it or hate it, the RIIR (Rewrite It In Rust) crowd certainly has a lot of momentum behind it.
For context, YJIT was developed at Shopify, based Maxime's thesis (see lazy basic block versioning). It has been merged into Ruby core, and she now has commit rights to Ruby.
The platform `problem` shouldn't be a problem, because you wouldn't use ruby outside of Linux, Windows, Mac, FreeBSD, OpenBSD and Rust has support for more than that[1]. Backwards compatibility isn't a concert either, Rust promised and kept it for some years (after 1.6).
The real problem is whether or not the expense of porting it is justified. Taking a look at the git repository of it (before it was merged into upstream), I understand why they want to rewrite it in Rust, the repository looks like a complete mess. I don't know if they want to use an existing JIT codegen like LLVM or Cranelift, or their own.
At least in what concerns AIX, IBM has migrated their compilers on top of LLVM, so some support is there.
No idea about HP-UX, it is impossible to find anything now, apparently HPE has buried almost everything about it. Completely unrelated to how good it used to be about 20 years ago.
I wasn't even referring to CRuby, I was referring to YJIT especially. For now, it only works on x86_64 Unix like systems (Linux and mac). I doubt that someone will ever port YJIT codegen to these "esoteric" architectures, so rust platform support is enough.
Afaik the only ones that's somewhat successfully used LLVM for a dynlang JIT is the Safari guys, and iirc that one was only for the highest optimization level (and that is an important hint on their direction and one that most dynlang JIT's should head).
The goals and constraints of practical dynlang JITs often differ a bit from those for statically typed languages. The JITs for statically typed languages are often fairly "straightforward" since the basic/primitive types are often known, and with the exception for dispatch optimizations, many large wins comes from register allocation,etc.
A dynlang JIT on the other hand far more often has unstable basic types that goes along with type guards,etc. You have a ton of optimizations in that regard that comes before it's even time to consider more than basic register allocation optimizations (See for example the recent posts on the new Erlang JIT).
In this particular case Maxime's PHd was a JS JIT that implemented something called Basic-Block-Versioning, you can see an early paper on this at https://arxiv.org/abs/1411.0352
My suspicion is that the existing codegens you mention comes with a lot of baggage that might be in the way of how a lazy codegen like they want should be structured. I wrote a small experimental lazy dynamic JIT once and the structure definitely got twisted around and "direct" access to all parts definitely was a plus.
It's a little disturbing that this YJIT was accepted into ruby 3.1 near the end of its cycle, just a couple months ago, and now it's being rewritten in a new language. Kind of a smell if you ask me.
"We are currently still early in our investigation of the Rust port. I am curious to know if you would also be open to YJIT being written in a language like Zig, or is that too new/experimental?" sounds half-baked...
They posted the reasoning - C is limiting them from finishing the job cleanly for more platforms. It sounds like a reasonable choice to move to something leaner before yjit grows too large and the sunk cost issue starts being raised.
Yeah to be honest, C was never our favorite pick to begin with. We initially wrote YJIT in C99 because that was what the rest of the CRuby codebase was written in. However, JIT compilers are very complex pieces of software. They need to do things like symbolic transformations on graphs, and deal with multiple different kinds of intermediate representations. Doing that in C, you get really bogged down with the details of manual memory management and rolling your own data structures. You can get all kinds of subtle bugs if you aren't careful.
We didn't push to upstream now, we were invited to upstream in CRuby and we accepted the invitation. As for the Rust rewrite, we just started to work on the prototype, and no decision had been made. The ticket linked in this thread is me essentially asking the Ruby core devs for permission to do so. We wanted to give everyone an opportunity to chime in.
Not sure what to tell you. Things don't aways happen in the most optimal order in the real world, sometimes plans change, but I'm fairly confident that we can make this work well.
How does that make graph transformations easier and avoid issues with memory management related to those? I tried looking in the docs at your link to see (because it looks a lot more like a syntax extension for an assembler than anything that helps with the problems the author noted), but the page notes,
> Sorry, right now there is no proper documentation included other than some Examples and of course the source code. The source is well documented, though (IMHO).
and the example doesn't seem to have anything relevant to the problems identified above.
And YJIT already work as it is? What is not to like?
The YJIT and the CRuby are different part of the project. Just like TruffleRuby is written Ruby and based on Java GraalVM. They are basically writing a version 2 of YJIT in different language.
It is interesting the discussion of Rust and Zig, which is the opposite spectrum of programming. It make sense if Ruby Cores are interested to develop / move to a new Ruby VM towards Rust. Not surprising considering Love2Code ( along with many other Ruby VM dev ) have complained over the years for CRuby's complexity. The Ruby Spec is a fairly recent thing done mostly by JRuby and TruffleRuby folks ( Correct me if am wrong ). If not, Zig on the other hand just works a lot better with old C code. One could argue Rust is Progressive and Zig is conservative.
Unfortunately it is still way too early for Zig.
I also wonder what is happening to TruffleRuby inside Shopify. And where is TenderJIT :)
My C complaints are well known, yet I don't see the need to make the build system even more complex, dropping platform support, for the benefit of yet another language, regardless of Rust or Zig winning in the end.
Then they shouldn't have merged the JIT to start with and just do their own Ruby implementation instead.
Several other Ruby JIT projects (Rubinius, JRuby, TruffleRuby, etc) have poured tons of resources, and while they perform very well, the adoption is still very low because keeping up with the main implementation takes tons of effort.
On the other hand YJIT managed to speedup real world codebases with pretty much 100% compatibility in under a year of development by a fairly small team.
Shopify already invest quite a lot in TruffleRuby, that's the new implementation moonshot you call for, YJIT is the more grounded, shorter term hedge.
I am fully aware of it, and spending now several months rewriting YJIT into Rust/Zig is supposed to get back that money (developer hours x $ per hour) exactly how?
I think you overestimate the size of the YJIT codebase. It's a very dense codebase in term on developer hours, translating it is far cheaper than it was to develop it from scratch.
It also doesn't come from out of nowhere, Alan already spent time experimenting on this idea with good results.
> is supposed to get back that money (developer hours x $ per hour) exactly how?
It's explained in the ticket, the goal is to have a better velocity once it's done.
You say you're fully aware of it but just the comment above you were advocating for something that took years to other VMs. Maybe have a little more faith in the YJIT team?
> I think you overestimate the size of the YJIT codebase.
So is it worth the hassle of introducing a new language? I like the enthusiasm argument, but tbh I don't see many Rust devs become interested developing core Ruby just because a small part of it is written in Rust.
My gut feeling is saying better to just keep it in C.
But anyway super excited about what the Shopify guys are doing, I want them to succeed no matter what approach they choose.
We aren't dropping support for any platforms. The Rust version of YJIT will work on all the platforms the C version was working on before, and eventually new platforms it wasn't working on before.
> Then they shouldn't have merged the JIT to start with and just do their own Ruby implementation instead.
See the thing is, building a new Ruby implementation from scratch would actually have been easier because we could have more of a clean room design. The problem is the compatibility. Making your new Ruby implementation 100% compatible with CRuby and the many Ruby gems found in the wild is extremely hard.
I respect your work a lot, still remember the days on D forums, however my experience in rewriting projects in consultancy hasn't been exactly that great.
Well, it was great for the money we got out of it, even though it set back the client for all the issues that came out of the process until they could proceed with the same velocity as before the rewrite took place.
Hence why I usually don't see the value in rewrites of any sort, and advocate for new language adoption to always go the clean room path.
So any tier 2+ platform for Ruby now needs to wait for Rust/Zig support before considering joining the YJIT effort, maybe a compromise the Ruby team is happy with given their replies, but it does have an impact nonetheless.
I think so. It has a similar syntax to Ruby, is statically typed with type inference, and has a much higher performance. I don't think it intends to nor can it replace Ruby. But it's still an attractive alternative for many projects.
I like Crystal, but it doesn't support the last three value propositions: it supports fewer platforms than Rust (in particular, no Windows outside of WSL) and has less community support/enthusiasm. It also has an obligate runtime, so linking to Crystal code from non-Crystal programs is problematic.
Hi, Artichoke author here. I feel very blessed to have Artichoke be mentioned in this ticket on Ruby's bug tracker. If y'all are interested to follow along, @artichokeruby on Twitter is the best spot to do it.
The mruby VM as used in Artichoke uses Artichoke's Rust implementations of `String`, `Symbol`, and `Array` (and many other core classes, these ones just happen to be used by the VM itself). The symbol table (which is an essential part of how classes and methods and `send` are implemented) is implemented using the `intaglio` crate which is part of the Artichoke project.
These implemented-in-Rust bits expose themselves to the remaining C with `extern "C"` APIs written in Rust that maintain ABI compat with the mruby code they replace.
The mruby VM is getting eaten by the Rust parts, so I don't think it's quite fair to say that Artichoke uses mruby as an off the shelf component. To fully move beyond mruby though to a native Rust VM will take time.
48 comments
[ 3.1 ms ] story [ 112 ms ] thread(Also, the last time I checked, cranelift emitted extremely suboptimal code. That's not their fault given the design constraints (it's a new project and they're aiming to beat LLVM's SelectionDAG in codegen performance), but it's probably not ideal given Ruby's long term performance goals. I'm also not sure how easy it would be to retrofit the LBBV[1] architecture of YJIT onto cranelift.)
[1]: https://dl.acm.org/doi/10.1145/3486606.3486781
It's refreshing to see a project explicitly call this out as a benefit for adopting Rust. Love it or hate it, the RIIR (Rewrite It In Rust) crowd certainly has a lot of momentum behind it.
It seems to be a proposal. The proposal was made by a user who was registered on 09/27/2021.
The text you quoted was included on his proposal. The project itself is not explicitly call that one out.
maximecb is the lead of YJIT project in Shopify.
The real problem is whether or not the expense of porting it is justified. Taking a look at the git repository of it (before it was merged into upstream), I understand why they want to rewrite it in Rust, the repository looks like a complete mess. I don't know if they want to use an existing JIT codegen like LLVM or Cranelift, or their own.
Ruby has support for plenty of "exotic" systems like HP UX, AIX etc. It's unclear how many people actually use it, but it's there.
> I don't know if they want to use an existing JIT codegen like LLVM or Cranelift, or their own.
Their own.
No idea about HP-UX, it is impossible to find anything now, apparently HPE has buried almost everything about it. Completely unrelated to how good it used to be about 20 years ago.
> you wouldn't use ruby outside of Linux, Windows, Mac, FreeBSD, OpenBSD
Isn't quite true.
My bad, that's really not what I understood from your initial statement. But yes agreed.
The only real downside is discussed on the ticket. Ruby is primarily installed from source, so requiring a second toolchain is not ideal.
The goals and constraints of practical dynlang JITs often differ a bit from those for statically typed languages. The JITs for statically typed languages are often fairly "straightforward" since the basic/primitive types are often known, and with the exception for dispatch optimizations, many large wins comes from register allocation,etc.
A dynlang JIT on the other hand far more often has unstable basic types that goes along with type guards,etc. You have a ton of optimizations in that regard that comes before it's even time to consider more than basic register allocation optimizations (See for example the recent posts on the new Erlang JIT).
In this particular case Maxime's PHd was a JS JIT that implemented something called Basic-Block-Versioning, you can see an early paper on this at https://arxiv.org/abs/1411.0352
My suspicion is that the existing codegens you mention comes with a lot of baggage that might be in the way of how a lazy codegen like they want should be structured. I wrote a small experimental lazy dynamic JIT once and the structure definitely got twisted around and "direct" access to all parts definitely was a plus.
Not sure what to tell you. Things don't aways happen in the most optimal order in the real world, sometimes plans change, but I'm fairly confident that we can make this work well.
You should be using dynasm https://luajit.org/dynasm.html instead of plain C.
> Sorry, right now there is no proper documentation included other than some Examples and of course the source code. The source is well documented, though (IMHO).
and the example doesn't seem to have anything relevant to the problems identified above.
The YJIT and the CRuby are different part of the project. Just like TruffleRuby is written Ruby and based on Java GraalVM. They are basically writing a version 2 of YJIT in different language.
Unfortunately it is still way too early for Zig.
I also wonder what is happening to TruffleRuby inside Shopify. And where is TenderJIT :)
My C complaints are well known, yet I don't see the need to make the build system even more complex, dropping platform support, for the benefit of yet another language, regardless of Rust or Zig winning in the end.
Then they shouldn't have merged the JIT to start with and just do their own Ruby implementation instead.
"just"...
Several other Ruby JIT projects (Rubinius, JRuby, TruffleRuby, etc) have poured tons of resources, and while they perform very well, the adoption is still very low because keeping up with the main implementation takes tons of effort.
On the other hand YJIT managed to speedup real world codebases with pretty much 100% compatibility in under a year of development by a fairly small team.
Shopify already invest quite a lot in TruffleRuby, that's the new implementation moonshot you call for, YJIT is the more grounded, shorter term hedge.
I think you overestimate the size of the YJIT codebase. It's a very dense codebase in term on developer hours, translating it is far cheaper than it was to develop it from scratch.
It also doesn't come from out of nowhere, Alan already spent time experimenting on this idea with good results.
> is supposed to get back that money (developer hours x $ per hour) exactly how?
It's explained in the ticket, the goal is to have a better velocity once it's done.
You say you're fully aware of it but just the comment above you were advocating for something that took years to other VMs. Maybe have a little more faith in the YJIT team?
So is it worth the hassle of introducing a new language? I like the enthusiasm argument, but tbh I don't see many Rust devs become interested developing core Ruby just because a small part of it is written in Rust. My gut feeling is saying better to just keep it in C.
But anyway super excited about what the Shopify guys are doing, I want them to succeed no matter what approach they choose.
Just because it's small in term of line of code, doesn't mean it isn't tricky to maintain and extend. It's all explained on the ticket.
> Then they shouldn't have merged the JIT to start with and just do their own Ruby implementation instead.
See the thing is, building a new Ruby implementation from scratch would actually have been easier because we could have more of a clean room design. The problem is the compatibility. Making your new Ruby implementation 100% compatible with CRuby and the many Ruby gems found in the wild is extremely hard.
Well, it was great for the money we got out of it, even though it set back the client for all the issues that came out of the process until they could proceed with the same velocity as before the rewrite took place.
Hence why I usually don't see the value in rewrites of any sort, and advocate for new language adoption to always go the clean room path.
So any tier 2+ platform for Ruby now needs to wait for Rust/Zig support before considering joining the YJIT effort, maybe a compromise the Ruby team is happy with given their replies, but it does have an impact nonetheless.
It seems to have hit a minor speedbump dealing with support for Windows.
https://github.com/artichoke/artichoke
It’s ruby, implemented in rust. Early days, but very interesting.
(I have no affiliation or involvement with it, just a fan)
https://twitter.com/artichokeruby
These implemented-in-Rust bits expose themselves to the remaining C with `extern "C"` APIs written in Rust that maintain ABI compat with the mruby code they replace.
The mruby VM is getting eaten by the Rust parts, so I don't think it's quite fair to say that Artichoke uses mruby as an off the shelf component. To fully move beyond mruby though to a native Rust VM will take time.
Besides that, I prefer Zig to Rust, as a long-term C programmer, but all up to you." - Matz (Yukihiro Matsumoto)
reference: https://bugs.ruby-lang.org/issues/18481#note-17