[HN Gopher] ZJIT is now available in Ruby 4.0
___________________________________________________________________
ZJIT is now available in Ruby 4.0
Author : ibobev
Score : 72 points
Date : 2025-12-26 17:04 UTC (5 hours ago)
(HTM) web link (railsatscale.com)
(TXT) w3m dump (railsatscale.com)
| ComputerGuru wrote:
| Non-ruby dev here. Can someone explain the side exit thing for
| me?
|
| > This meant that the code we were running had to continue to
| have the same preconditions (expected types, no method
| redefinitions, etc) or the JIT would safely abort. Now, we can
| side-exit and use this feature liberally.
|
| > For example, we gracefully handle the phase transition from
| integer to string; a guard instruction fails and transfers
| control to the interpreter.
|
| > _(example showing add of two strings omitted)_
|
| What is the difference between the JIT safely aborting and the
| JIT returning control to the interpreter? Or does the JIT abort
| mean the entire app aborts (i.e. I presumed JIT aborting means
| continuing on the interpreter anyway?)
|
| (Also, why would you _want_ the code that uses the incorrect
| types to succeed? Isn't abort of the whole unit of execution the
| right answer here, anyway?)
| nt591 wrote:
| Dynamic languages will allow a range of types through
| functions. JITs add tracing and attempt to specialize the
| functions based on the observed types at runtime. It is
| possible that later on, the function is called with different
| types than what the JIT observed and compiled code for. To
| handle this, JITs will have stubs and guards. You check the
| observed type at runtime before calling the JITted code. If the
| type does not match, you would call a stub to generate the
| correct machine code, or you could just call into the
| interpreter slow path.
|
| An example might be the plus operator. Many languages will
| allow integers, floats, strings and more on either side of the
| operator. The JIT likely will see mostly integers and optimize
| the functions call for integer math. If later you call the plus
| operator with two Point classes, then you would fall back to
| the interpreter.
| tekknolagi wrote:
| In this case, we used to abort (i.e. abort(); intentionally
| crash the entire process) but now we jump into the interpreter
| to handle the dynamic behavior.
|
| If someone writes dynamic ruby code to add two objects, it
| should succeed in both integer and string cases. The JIT just
| wants to optimize whatever the common case is.
| ComputerGuru wrote:
| I guess I'm confused why an actual add instruction is emitted
| rather than whatever overloaded operation takes place when
| the + symbol (or overloaded add vtable entry) is called (like
| it would in other OOP languages).
| tekknolagi wrote:
| If all you're doing is summing small integers---frequently
| the case---it's much preferable to optimize that to be fast
| and then skip the very dynamic method lookup (the slower,
| less common case)
| zingar wrote:
| Presumably other languages with JIT do exactly the same
| thing?
| zingar wrote:
| I'm assuming that when you talk about crashing processes as
| the status quo you're referring to earlier versions of zjit
| rather than current Ruby on yjit? Because I've never seen a
| Ruby process crash because + was called with different
| arguments.
| endorphine wrote:
| It would be useful to explain why ZJIT exists given that there's
| already YJIT.
|
| Also, what's the long-term plan for YJIT.
| fourseventy wrote:
| I just upgraded my prod apps to run on YJIT so I'm annoyed at
| this announcement. Feels like javascript-esque runtime churn
| tekknolagi wrote:
| Earnestly: why are you annoyed? I tried to make it clear that
| you don't have to make any changes. If you want, you can try
| ZJIT (which should not be anything other than a one character
| change), but you don't have to.
| fourseventy wrote:
| Because now YJIT is deprecated and at some point in a year
| or two I will have to have my team go through and switch
| everything from YJIT to ZJIT. So it's creating random tech
| debt busywork that will suck up dev resources that could
| better be used building features.
|
| In isolation, having to switch from YJIT to ZJIT isn't that
| bad, but this same type of churn happens across so much of
| the frameworks and technologies that my company uses that
| in aggregate it becomes quite an annoyance.
| tekknolagi wrote:
| YJIT is _not_ deprecated. That word has a specific
| meaning in Ruby. You can continue to use YJIT.
|
| With any luck, this performance in the next year or two
| will be enough to make it a happy change. "Damn, free
| money" etc
| pjmlp wrote:
| Most JIT compiled languages have multiple implications,
| each pushing each other forward.
| riffraff wrote:
| ZJIT exists because it's a more traditional design and there's
| hope more people will have a easier time contributing[0]. Given
| that, it seems YJIT will become unnecessary if ZJIT succeeds.
|
| 0: https://railsatscale.com/2025-05-14-merge-zjit/
| baggy_trough wrote:
| The explanation is in the second paragraph of the article.
| awestroke wrote:
| I recently rewrote one of my rails apps in rust. Used Claude 4.5
| Opus heavily and it was very fast.
|
| One thing that's struck me with the new code is that's its so
| easy to follow compared to rails. It's like two different
| extremes on the implicit-explicit spectrum. Yet it's not like I
| have tons more boilerplate code now, I think I have maybe 10 or
| 20% more SLOC than before.
|
| I'll probably do this with my other rails apps as well.
| losteric wrote:
| Was your app converted to use some Rust framework, or just
| Rust?
| awestroke wrote:
| I use Axum+SQLx and for html templates I use Maud. The plan
| is to move to Dioxus as a step 2
| hu3 wrote:
| Any language with a half decent typing system would have
| improved the easiness to follow code around. C#, Go, PHP,
| TypeScript, etc.
|
| Ruby/Rails is awesome but it's a bit too magical sometimes and,
| lacking types by default, doesn't help either.
| azuanrb wrote:
| You're comparing a language with a framework. Better comparison
| would be Rust (with your choices of libraries) vs Ruby (with
| your choices of libraries).
|
| If you want to compare with Rails, you need to compare with
| battery included Rust frameworks with equivalent batteries and
| convention.
| awestroke wrote:
| I literally rewrote the whole app, so I think I have a pretty
| great basis for comparison
| aapoalas wrote:
| I'm a little sad to see YJIT go down in favour of a traditional
| design. (Yes, YJIT isn't "deprecated" but the people working on
| it are now mainly working on something else. That's hardly a
| great place to be in.)
|
| I'm personally quite interested in trying out an LBBV JIT for
| JavaScript, following in Chevalier-Boisvert's Higgs engine's
| footsteps. The note about a traditional method JIT giving more
| code for the compiler to work with does ring very true, but I'd
| just like to see more variety in the compiler and JIT world.
|
| Like: perhaps it would be possible to conjoin (say) an LBBV with
| a SoN compiler such that LBBV takes care of the quick, basic
| compilation and leaves behind enough data such that a SoN
| compiler can be put to use on the whole-method result once the
| whole method has been deemed hot enough? This is perhaps a
| totally ridiculous idea, but it's the kind of idea that will
| never get explored in a world with only traditional method JITs.
| tekknolagi wrote:
| In that scenario, what would you hope to get out of the LBBV?
| aapoalas wrote:
| Preface: I am not a compiler engineer at all, so I'm just
| spitballing silliness here.
|
| Avoidance of type feedback counters and such. Get LBBV to
| clean out the redundant type checks (Higgs proved this well,
| avoiding something like >90% of them) and produce a format,
| perhaps a high-level bytecode or just an HIR, that can be
| used as an input to start a method-level JIT compilation.
|
| So, LBBV gives the quick and easy basic block compilation and
| cleans up the very easy stuff but leaves enough information
| so that a follow-up compiler can still use it as input.
___________________________________________________________________
(page generated 2025-12-26 23:01 UTC)