[HN Gopher] RJIT, a new JIT for Ruby
       ___________________________________________________________________
        
       RJIT, a new JIT for Ruby
        
       Author : pmarin
       Score  : 243 points
       Date   : 2023-03-07 11:29 UTC (1 days ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | sambostock wrote:
       | Several points discussed in these comments are addressed by the
       | author in the linked https://bugs.ruby-lang.org/issues/19420
        
       | yxhuvud wrote:
       | Are they adding a new jit each version now?
        
         | pmarin wrote:
         | It is replacing MjIT by the same author.
        
       | beders wrote:
       | Is jRuby still a thing? I didn't see it in the perf comparisons.
        
         | Lio wrote:
         | Yes it's definitely still a thing.
         | 
         | They actually had a new release at the beginning of March.
         | 
         | https://www.jruby.org/2023/03/08/jruby-9-4-2-0.html
        
       | titzer wrote:
       | Honest question, I do not know Ruby's semantics well. But, as
       | someone who has worked on many JITs in the past, how is it in
       | these results, _three different_ JITs failed at getting more than
       | a 2x performance improvement? Normally, a JIT is a 10-20x
       | improvement in performance, just from the simple fact of removing
       | the interpreter dispatch loop. What am I missing?
        
         | seunosewa wrote:
         | Dynamic language, which allows anything to be changed at any
         | time.
        
           | titzer wrote:
           | It's clearly more complicated than that. JavaScript is also
           | highly dynamic and JITs there often give 10-100x speedup.
        
             | nicoburns wrote:
             | My impression is that Ruby is _more_ dynamic than pretty
             | much everything else. I think this is true in terms of
             | language features, but also in terms of the style that code
             | is written in practice.
        
         | tenderlove wrote:
         | I'll take a stab at this.
         | 
         | YARV (Ruby's VM) is already direct threaded (using computed
         | gotos), so there's no dispatch loop to eliminate. YARV is a
         | stack based virtual machine, and the machine code that YJIT
         | generates writes temporary values to the VM stack. In other
         | words, it always spills temporaries to memory. We're actively
         | working on keeping things in registers rather than spilling.
         | 
         | Ruby programs tend to be extremely polymorphic. It's not
         | uncommon to see call sites with hundreds of different classes
         | (and now that we've implemented object shapes, hundreds of
         | object shapes). YJIT is not currently splitting or inlining, so
         | we unfortunately encounter megamorphic sites more frequently
         | than we'd like.
         | 
         | I'm sure there's more stuff but I hope this helps!
        
       | CyberDildonics wrote:
       | These comparisons seem to be to other ruby implementations. How
       | does this compare to LuaJIT ?
        
         | jhatemyjob wrote:
         | It probably doesn't. LuaJIT is still state-of-the-art, despite
         | being in maintenance mode for almost a decade...
        
         | brokencode wrote:
         | That is kind of irrelevant if you are running a Ruby
         | application. And I think that if a developer is looking to
         | start working on a new web server where performance is a
         | significant concern, they are more likely to look at Go, Rust,
         | or even JavaScript rather than either Lua or Ruby.
        
           | ecshafer wrote:
           | Performance is a weird metric for a web application. You can
           | say Go or Rust will be more performant than Ruby or Lua,
           | sure. But with web applications so often your performance has
           | nothing to do with the language or hiccup. But you aren't
           | just processing N requests and spitting out a response, you
           | are communicating with other services and databases. IO is
           | almost always a bigger source of latency in response than
           | language speed, until you have a large enough service where
           | you can start to worry about those small issues. Before the
           | Developer performance matters more.
        
           | CyberDildonics wrote:
           | LuaJIT should be much faster than javascript
           | 
           | Also how slow is still ok? People can talk about things that
           | aren't 'performance sensitive' but at some point it's going
           | to matter. If a program is serving up web pages, that's an
           | interactive application and people are waiting on the
           | program.
        
             | brokencode wrote:
             | JavaScript is so much more widely used for so many
             | applications that it's hard to justify LuaJIT, even if it
             | is faster. They're both much faster than most scripting
             | languages like Python and Ruby.
             | 
             | If JavaScript really isn't fast enough, then I think you
             | should be thinking about something like Go or Rust instead.
             | 
             | LuaJIT is an incredible piece of technology, but in this
             | performance tier JavaScript has won out due to sheer
             | ubiquity and the size of its ecosystem.
             | 
             | Edit: I never said slow was okay. I'm not advocating for
             | slow, I'm just saying that the target audience for RJIT is
             | developers who are already using Ruby. For significantly
             | better speed, you probably want to look elsewhere.
        
             | winrid wrote:
             | Even a slow framework is still fast for humans. My Django
             | site renders the homepage in 10ms, and django is kind of in
             | the realm of Rails performance wise.
             | 
             | It's all about cost, really. But you can just tell Nginx to
             | cache pages and then it's not a problem for the vast
             | majority of use cases.
        
             | JohnBooty wrote:
             | Also how slow is still ok? People can talk about things
             | that aren't 'performance sensitive' but at some point
             | it's going to matter
             | 
             | Done a fair amount of Rails perf tuning over the years. One
             | of my favorite things to work on.
             | 
             | "Fast enough" for me, is when your web framework is
             | _nowhere near_ your bottleneck. On your average web app
             | endpoint you 're probably spending 95-99% of your time on
             | external calls to Redis/Postgres/etc and Postgres is
             | probably your specific bottleneck.
             | 
             | For _these_ apps, Rails is most definitely fast enough. You
             | could rewrite your app layer in well-tuned C or assembly
             | and guess what, it 's getting maybe 1-5% faster if you're
             | lucky. Maybe you get from 100ms down to 95ms and all you
             | had to do was rewrite 100,000 lines of Ruby in 200,000
             | lines of C.
             | 
             | For other cases, obviously, maybe Rails is your bottleneck.
             | Maybe you're providing a read-only API and everything is
             | cachable in RAM. Rails will be fast, maybe 10ms per
             | request, but a faster framework can spew out responses in
             | 2ms and now you have 5x the capacity and your P95s during
             | peak hours are really smoothed out.
        
         | nirvdrum wrote:
         | Do you have a particular task in mind? The languages have
         | different semantics and that has an effect on performance.
         | E.g., in Ruby nearly everything is a method call. Ideally a JIT
         | would eliminate that overhead, but you'll likely see varying
         | degrees of performance depending on what your task is. And
         | that's before you get to core library methods, which often are
         | written in C and not handled by the JIT.
        
       | foxandmouse wrote:
       | Is there any use of ruby in the deep learning space? It's my
       | language of choice, but Python seems to be ubiquitous.
        
         | cdiamand wrote:
         | It looks like there was a little movement in the space not too
         | far back:
         | 
         | https://ankane.org/new-ml-gems
         | 
         | But I don't know how often this is used in production. I've
         | ended up training the models in python and then loading them in
         | Ruby.
        
       | rco8786 wrote:
       | Why would one use this over YJIT?
        
         | maxfurman wrote:
         | To add to sibling comments, YJIT needs the Rust compiler, so if
         | you have to build your own Ruby binary, and you have to build
         | it on a system where you can't get a Rust compiler, then RJIT
         | will make your life easier. Not sure how common this is in the
         | real world though.
        
         | sparker72678 wrote:
         | Right now maybe you wouldn't, very much (though you should
         | profile your code to see which performs better). But having a
         | JIT written in Ruby potentially makes further development on it
         | more accessible to the community. We will see!
        
           | weatherlight wrote:
           | seems strange, since most rubyist aren't compiler engineers.
           | I feel like you'd still want to keep writing your compiler in
           | Rust, and try to eek out your performance there.
           | 
           | I'm still scratching my head, other than accessibility, Why
           | Ruby over Rust.
           | 
           | Note: I'm a Ruby dev, I don't know Rust. I've written few toy
           | interpreters in Elixir and OCaml. This is my very limited
           | understanding of compiler design, etc.
        
             | sudhirj wrote:
             | Using a JIT seems different from creating a JIT, though,
             | and doesn't seem to require compiler engineers. From what I
             | understand this converts Ruby code directly into C object
             | code? Or does it transpile to C and then compile it? Either
             | way, transpiling doesn't seem as complicated as compiling.
        
             | sparker72678 wrote:
             | I think you could say yjit fills that role; in any case,
             | having multiple active jit projects leaves open a lot of
             | room for experimentation. In the end, you might be right
             | and rjit will fade away -- we will see!
        
             | simlevesque wrote:
             | > most rubyist aren't compiler engineers.
             | 
             | You could say that about any language.
        
               | weatherlight wrote:
               | Can you say that about Standard ML? theres traditionally
               | certain langs with particular ergonomics that lend them
               | self to this kinda work. Like having (parser, lexer libs
               | as apart of the lang's stdlib)
               | 
               | Rust's ADT seem particularly useful in this context. it
               | really makes refactoring a breeze. (OCaml has a similar
               | type system.)
               | 
               | Your point is generally true though.
        
               | ColonelPhantom wrote:
               | What if you turn it around? "Most compiler engineers
               | aren't Rubyists."
               | 
               | I don't know if that's true or not, but I imagine most
               | compiler engineers tend to be more engrossed in languages
               | like *ML, Rust, or Haskell. Or languages that are common
               | in general, like C++ or Python. Ruby isn't that popular
               | (outside of the Rails niche at least?), and it doesn't
               | fit very well in a compiler niche either, I think.
        
               | greenpeas wrote:
               | But why would a compiler engineer that's not familiar
               | with Ruby work on Ruby? They have so many other languages
               | to choose from. I don't necessarily think that writing a
               | JIT in Ruby is a good strategy to attract people to work
               | on it, but if you are going to attract compiler people,
               | you most likely want those who are also Rubyists.
        
               | JonChesterfield wrote:
               | Speaking mostly for myself, at least some compiler
               | engineers like difficult source or target languages. The
               | very dynamic ones are difficult to compile efficiently
               | and thus more interesting than some alternatives. Plus
               | Ruby hasn't had the attention paid to it that JavaScript
               | has so the design space is closer to greenfield. I can
               | see the attraction.
        
         | riffraff wrote:
         | there is no concrete reason to use it right now, and it's
         | marked as experimental, but being pure ruby would allow for
         | exploration and experimentation more easily.
        
         | nerpderp82 wrote:
         | Being pure Ruby, it should make it much easier for Ruby
         | programmers to hack on. I am really impressed with the latency
         | numbers. This will be some enjoyable code to read. It also
         | sounds like through this and other's work, that Ruby will have
         | a defacto JIT interface to the VM. This could open up the door
         | for domain or framework specific jits, AI powered jit, jits
         | that reload their past state, etc.
         | 
         | I am a huge Rust fan, but going up stack and writing your jit
         | as first party is pretty cool. Maybe after RJIT is well
         | factored, the internals could done in Rust again.
         | 
         | Really happy for Ruby!
        
       | mabbo wrote:
       | Does RJIT get JIT compiled... by itself? That would be lovely in
       | the sense that as RJIT finds more optimizations to speed up code,
       | it would become itself faster.
        
         | extrememacaroni wrote:
         | How many levels of JIT would be too many I wonder
        
           | ignoramous wrote:
           | AOT + JIT + PGO (profile guided optimization) is where things
           | are at.
        
         | aardvark179 wrote:
         | I mean, that's what happens with JITs like Graal. It can
         | present a warmup issue which is part of the reason Graal did so
         | much work to enable AOT compilation.
        
       | sparker72678 wrote:
       | I love all the attention Ruby performance is getting lately!
        
         | Qem wrote:
         | Congrats to the Ruby developers, now they are on the way to
         | have more than one production-grade JIT available in the
         | reference implementation. I hope Python catches up soon, and
         | the proposal to merge CPython and Pyston goes forward.
        
       | jhoechtl wrote:
       | Boy I lost track of all the Ruby Jit attempts.
       | 
       | According to the computer language shootout all micro-
       | optimizations
        
         | ezekg wrote:
         | YJIT sped up my Rails app by about 30%. It has a memory
         | overhead, but it's worth it.
        
           | maxime_cb wrote:
           | For anyone curious, we've been working to reduce the memory
           | overhead and have added some stats to keep track of memory
           | usage over time. On this graph, you can see a comparison with
           | the CRuby interpreter:
           | 
           | https://speed.yjit.org/memory_timeline#railsbench
        
             | greenpeas wrote:
             | What happened on jun 14? (the dramatic drop in memory
             | usage)
             | 
             | Edit: I guess this (https://github.com/ruby/ruby/pull/5944)
             | PR was merged.
        
               | maxime_cb wrote:
               | Yes. Prior to that point we used to allocate a large
               | chunk of executable memory upfront. We switched to
               | mapping that memory on demand, and that alone was a huge
               | improvement.
        
           | nerpderp82 wrote:
           | That is huge, did it also reduce(~~bring in~~) tail latency?
           | 
           | *edit, fix confusing vernacular
        
             | ezekg wrote:
             | Nope -- P99 also decreased. I've heard similar things for
             | other Rails apps as well.
        
               | nerpderp82 wrote:
               | Sorry, in my usage "bring in" means move tail latency
               | P99/P100 more to the left not as introduce, I'll be more
               | clear next time.
               | 
               | So yes! That is great news.
        
         | nirvdrum wrote:
         | https://ruby-compilers.com/ is a comprehensive list of the
         | various Ruby compilers. There's a table summarizing them along
         | with detailed descriptions for some.
        
       | barrenko wrote:
       | Is there any alternative to RubyMine as an IDE for Ruby newbies?
        
         | nirvdrum wrote:
         | For IDEs, Shopify provides the Ruby Extension Pack for VS Code
         | [1]. Closely related to RubyMine is the Ruby plugin for
         | IntelliJ IDEA. Those seem to be the biggest set of IDEs for
         | several languages. There's Ruby support for editors like Vim
         | and emacs, but that's a different experience from an IDE.
         | 
         | [1] -- https://github.com/Shopify/vscode-shopify-ruby
        
         | Alifatisk wrote:
         | Vscode + solargraph + ruby extension
        
       | zac23or wrote:
       | I work everyday with Rails.
       | 
       | In my experience, Ruby is not super slow.
       | 
       | In my machine, I can create 1M of empty hashs on 0.17sec.
       | Benchmark.measure{1000000.times{Hash.new}}       @total =
       | 0.1706789999999998s
       | 
       | It's very good for a dynamic language.
       | 
       | But ActiveRecord (and Rails) are incredibly slow.
       | 
       | In my machine in 0.17sec only 2000 Models can be created.
       | Benchmark.measure{2000.times{User.new}}       @total =
       | @total=0.17733399999999833.
       | 
       | Some SQL+Network runs in less than 10ms, in these cases Active
       | Models creation is slower than that.
       | 
       | Yes, Rails can be slower than database access.
        
         | Alifatisk wrote:
         | I think the idea of Ruby being slow was back in 1.9, this was
         | way before Matz announced the goal of 3x3 with Ruby 3.
        
           | jeltz wrote:
           | No, it is actually from even before that, from Ruby 1.8 which
           | did not have a proper VM.
        
         | Someone wrote:
         | I would guess _Hash.new_ does little more than one or two
         | allocations (one for the object, possibly one for an empty hash
         | table that can later be resized), and if it did two
         | allocations, linked one to the other.
         | 
         | If so, that probably is more a benchmark of your memory
         | allocator, which probably is written in C than of ruby.
         | 
         | I also guess you ran the benchmark from a new ruby instance.
         | That means memory wasn't fragmented. That certainly doesn't
         | make the allocator's job more difficult.
        
       | Alifatisk wrote:
       | > ...many methods are direct translations of the Rust code into
       | Ruby.
       | 
       | Impressive
        
       | l_theanine wrote:
       | I'd definitely be more apt to have this as part of production
       | system instead of the Rust one.
       | 
       | Rust has got to be the ugliest, most unfriendly programming
       | language I've ever laid my eyes on. And I wrote Perl for 10+
       | years, so that's really quite a feat of aesthetics failure.
       | 
       | Anyhow, I'm pretty impressed with the performance thus far, I
       | like the idea of having multiple JITs available for a single-
       | language ecosystem, regardless of how disgusting the language
       | used to implement them. I think having competition means that
       | there will be a race to the bottom and towards the "center" of
       | general work. It's already really cool to see how the different
       | approaches have clear preferences of the tasks they excel at and
       | where they fall short.
       | 
       | This is hugely valuable because it pushes Ruby forward for
       | everybody, and will hopefully result in not only a faster Ruby
       | for X, but a faster Ruby for everything, which is just an
       | objectively good thing.
       | 
       | Python is in a weird spot in this arena, because it is very
       | clearly and very strongly orienting itself to continue to
       | dominate practical data science work, and that means the need for
       | JITs to handle regular jobs like text munging and whatnot fall by
       | the side in order for the latest NumPy and Jax stuff, whatever is
       | the current hot shit in the AIverse. Ruby doesn't suffer from
       | that because it's pretty solidly lodged in the web development
       | sphere, while also having a capable presence in netsec tools,
       | application scripting, and probably a few more areas that I'm not
       | aware of.
       | 
       | If you're interested in some of cutting edge Python stuff, I'd
       | recommend taking a look at exaloop/Codon. Codon will soon be able
       | to output Python extensions that are compatible with Python's
       | setuptools, so it will soon be possible to just include some
       | .codon files with your project, use setup.py, and have decorators
       | that can (literally) 100x your hot loops.
        
         | bluedays wrote:
         | Be honest, you mostly write this post so you can say you hate
         | Rust, didn't you?
        
           | l_theanine wrote:
           | I guess you'll have to find an adult to read the whole thing
           | to you to find out. I can see you got your feelings hurt
           | after the first two sentences. :(
        
       | pawelduda wrote:
       | Quite interesting. My takeaway is that it can be on par with YJIT
       | or even outperform it despite being in early development.
       | 
       | Btw one project I work on switched to YJIT in production and
       | there are no problems so far (but no noticeable perf gains
       | either)
        
         | stanislavb wrote:
         | Yeah, I have the same experience with SaaSHub. I moved to YJIY
         | a week ago - no issues, but no noticeable perf gains either
         | (unfortunately).
        
         | weatherlight wrote:
         | What about in memory usage?
        
         | maxime_cb wrote:
         | You're right that the peak performance could be on par (or even
         | better), but, and I acknowledge that I'm biased since I'm tech
         | lead of the YJIT team, my takeaway is:
         | 
         | 1. Kokubun, who works with us on the YJIT team, is leveraging
         | insights he's learned while working on YJIT to build this. He
         | has said so in his tweets, some of the code in RJIT is a direct
         | port of the YJIT's code to Ruby. This is his side-project.
         | 
         | 2. One of the challenges we face in YJIT is memory overhead.
         | It's something we've been working hard to minimize. Programming
         | RJIT in Ruby is going to make this problems worse. Not only
         | will it be hard to be more memory-efficient, you're going to
         | increase the workload of the Ruby GC (which is also working to
         | GC your app's data).
         | 
         | 3. Warm-up time will also be worse due to Ruby's performance
         | not being on par with Rust. This doesn't matter much for
         | smaller benchmarks, but for anything resembling a production
         | deployment, it will.
         | 
         | On your second point, if you're not seeing perf gains with
         | YJIT, we'd be curious to see a profile of your application, and
         | the output of running with `--yjit-stats`. You can file an
         | issue here to give us your feedback, and help us make YJIT
         | better: https://github.com/Shopify/yjit/issues
        
       ___________________________________________________________________
       (page generated 2023-03-08 23:00 UTC)