Hacker News new | ask | show | jobs
by erikrothoff 1244 days ago
I'm pretty sure (there isn't a lot of info on how to enable it in production workloads) we're running YJIT in production and didn't notice any improvement in load times, CPU usage or anything really. If any slightly higher metrics across the board.

We're using Rails as API and web frontend with a pretty high number of requests per second, so I was hoping we'd see something. Does anyone have any experience rolling it out?

4 comments

    Does anyone have any experience rolling it out? 
FWIW, the linked article has prod benchmarks.

    I'm pretty sure (there isn't a lot of info on how 
    to enable it in production workloads) we're running 
    YJIT in production
Sure you're running it? You have to compile with YJIT support, and then pass the command line arg. (It doesn't support YJIT out of the box because, I presume, they didn't want to force a Rust dependency on everybody?)

Here's how on MacOS + asdf, others will be similar. Note that I think --enable-yjit might work (but do nothing) even if you don't have support compiled in.

    # if you already installed 3.2.0
    asdf uninstall ruby 3.2.0
    
    # install Rust
    asdf plugin-add rust
    asdf...      
    
    # now install Ruby 3.2
    asdf plugin-update ruby
    export RUBY_CONFIGURE_OPTS=--enable-yjit
    asdf install ruby 3.2.0
    asdf global ruby 3.2.0

    # verify it's installed and ready
    ruby --enable-yjit --version

    # now run your workload
    ruby --enable-yjit foo.rb
Yes, I'm pretty sure because:

1. Compiled with jyjit confrmed (ruby --yjit) returns the correct value

2. RubyVM::YJIT.enabled? returned true

But the information available on how to confirm YJIT is running is not super clear. Since I didn't notice any improvements I started wondering.

Sounds like you're running it!

    Since I didn't notice any improvements I started wondering. 
It only speeds up Ruby itself. In a "real world" web app performance, performance is mostly dominated by Ruby/PHP/Java/whatever sitting around and waiting for database queries to execute.

So for a lot of web workloads the perf increase won't be huge. If your average endpoint is e.g. 5ms of Ruby and 500ms of DB queries, your max speedup will be negligible if you switch to YJIT... or even if you rewrite the whole app in hand-optimized assembler.

(Also gets to the root of why perf-based criticism of Ruby is usually dumb, IMHO. It's usually not the problem)

There might be a number of reasons why YJIT isn't effective in your application.

The best way to know it to enable YJIT runtime statistics [0] to see whether YJIT is doing a lot of side exits and if so why. But it requires a bit of YJIT knowledge to interpret them.

It's also entirely possible that your application isn't CPU bound in the first place, or that it's bottlenecked by things YJIT can't do anything about (e.g. GVL).

That's close to impossible to guess what it might be without being able to instrument the application.

[0] https://github.com/ruby/ruby/blob/df6b72b8ff7af16a56fa48f3b4...

The top comment in HN is one where the poster is not 100% is using the thing we are discussing.
> I'm pretty sure (there isn't a lot of info on how to enable it in production workloads

The YJIT is not on by default in ruby 3.2, you have to specifically enable it. If you aren't sure if you have enabled it... what makes you pretty sure you have enabled it? It seems possible you have not enabled it, if you aren't confident you know how to do so?

I am not using it yet myself, and don't want to put any possibly incorrect info here about how to enable it. I agree that it's not clear to me where to find this documented. I am pretty sure it is not on by default.

Also worth noting that the Ruby 3.2 Docker image can't run with YJIT yet, so if your production setup is in a container, it's most likely not using YJIT.

GitHub issue link here: https://github.com/docker-library/ruby/pull/398

It was supported immediately for Alpine based images because rustc 1.60+ was available in that distro.

Initially when 3.2 was released Debian based images didn't support it because Bullseye ships with a version of rustc that's too old to compile YJIT.

But https://github.com/docker-library/ruby/commit/6db728e addressed this a few days ago by installing a pre-compiled version of rustc straight from Rust and YJIT is available in Debian now.

You can confirm if it's available to use by running:

    $ docker container run --rm ruby:3.2.0-slim-bullseye ruby --enable-yjit -v
    ruby 3.2.0 (2022-12-25 revision a528908271) +YJIT [x86_64-linux]
If it weren't available then you would get an unknown argument error.

    $ docker container run --rm ruby:3.2.0-slim-bullseye ruby --enable-yjit -v
    ruby 3.2.0 (2022-12-25 revision a528908271) +YJIT [aarch64-linux]
Runs on Arm as well!
That's not correct:

    $ podman run -it -e RUBYOPT="--yjit" ruby:3.2
    irb(main):001:0> puts RUBY_DESCRIPTION
    ruby 3.2.0 (2022-12-25 revision a528908271) +YJIT [aarch64-linux]
    => nil
Yes, I'm pretty sure because:

1. Compiled with jyjit confrmed (ruby --yjit) returns the correct value

2. RubyVM::YJIT.enabled? returned true

I didn't notice any improvements at basically so I can't really say _for sure_ if it was working as intended.

RubyVM::YJIT.enabled? returning true does seem to settle it, and no need to say "pretty sure" with qualifications!
The gist of it is that you need to build Ruby with `RUBY_CONFIGURE_OPTS=--enable-yjit` true and verify it is enabled with `ruby --enable-yjit --version` and then finally run your code with `ruby --enable-yjit foo.rb`. For Rails you'd want to just edit the executable Rails scripts like `bin/rails` to include that flag.
Thanks! Is there a "canonical" place to find this documented, like with the ruby distro?

> For Rails you'd want to just edit the executable Rails scripts like `bin/rails` to include that flag.

Alternately, you can edit your shell init scripts to put the correct thing in `RUBY_OPTS` ENV variable, yes?

I would consider this preferable to editing executable rails scripts with implementation-specific stuff (and then hoping that the app is always launched with those edited shell scripts, which I'm not sure eg `bundle exec rails` will do). I don't think editing Rails scripts is probably the "right" or recommended way to do this.

But I'm not going to try to specify what you put in `RUBY_OPTS` since i haven't done it myself and don't want to spread misinformation! (Again, is this documented in a centralized trustworthy place? As I try to google it... I am somewhat frustrated with the ruby documentation situation!)

Perhaps this is what you are looking for:

https://github.com/ruby/ruby/blob/master/doc/yjit/yjit.md

thank you!
We're using puma and set the RUBYOPT=--yjit in a systemd environment variable for our puma service.

The performance impact of enabling yjit was quite obvious in our charts. Another giveaway that it's properly enabled is the servers will use consume more memory.

You can also set RUBY_YJIT_ENABLE=1 to enable it.
That’s what I ended up using:

passenger_env_var RUBY_YJIT_ENABLE 1;