[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)
 
web link (github.com)
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)