YJIT: A Look at Shopify’s CRuby JIT

Shopify has been developing YJIT, a just-in-time compiler for CRuby, and it’s now set to ship as part of Ruby 3.1. The project’s goal is straightforward: convert frequently executed Ruby code into optimized native machine code. Early indications are that it works—the team reports a 20% speedup on a simple Rails benchmark and error-free runs across Shopify’s own unit test suite for its main Rails application, as well as similar tests at GitHub.

Building and testing YJIT is no different from building any other CRuby release, since it’s being merged directly into the Ruby codebase. The process is a useful exercise for anyone who wants to experiment with YJIT, use it with custom configuration flags, or simply understand how to compile CRuby from source.

Compiling CRuby with YJIT

Before you can build Ruby from source, you’ll need the usual development prerequisites: Autoconf, make, OpenSSL headers, and a C compiler (GCC or Clang). On macOS, Xcode plus Homebrew covers all of these:

brew install [email protected] [email protected] # Unless you already have them

Note that Ruby does not work with OpenSSL 3, which Homebrew installs by default, so the explicit version pin is important. There have also been reports of Autoconf 2.71 failing with Ruby, making 2.69 the safer choice on that platform. On Linux, a package set like Debian’s build-essential plus autoconf and libssl-dev will suffice. Keep in mind that installing Ruby from a package manager is not enough to build a new Ruby—those packages don’t include the necessary development headers.

Once the prerequisites are in place, you can clone the repository and build a local YJIT-enabled Ruby. After that, it’s a matter of switching to it through your version manager: chruby ruby-yjit for chruby users, the analogous command for rbenv, or a mount step for rvm.

Verifying the Installation

There are two quick ways to confirm YJIT is active. First, run ruby --enable-yjit -v. If the build is correct, the output will show that YJIT is enabled. A warning that enable-yjit is unrecognized usually means your shell is still pointing at a different Ruby binary. Alternatively, you can launch irb and check whether the YJIT module exists.

Since YJIT is off by default, you’ll need to enable it for every session. Either export RUBYOPT='--enable-yjit' or export RUBY_YJIT_ENABLE=1 does the trick.

Running and Testing

YJIT’s reliability on Shopify’s and GitHub’s unit test suites suggests it can handle most codebases. To see what it does for your own project, run something like rake test with and without YJIT enabled and compare runtimes. Shopify also maintains a benchmark suite, yjit-bench, with the same workloads used on its public benchmark results page. A typical run looks like:

ruby -Iharness benchmarks/activerecord/benchmark.rb

The YJIT documentation included in the Ruby source tree describes command-line parameters and build-time options for more targeted testing.

Production Readiness and Stability

YJIT is functional but not yet fully optimized. Shopify has observed only minor speedups—around 6%—on a canary deployment serving a small percentage of real traffic to a production web application. That’s a meaningful gain when scaled across many servers, but it’s still shy of the benchmark results. Unit tests, benchmarks, and limited real-world traffic constitute a promising start, but broader adoption data will only come with the Ruby 3.1 release and wider community testing.

Should YJIT crash or produce errors, the team wants to hear about it. Bugs, performance complaints, or notable speed improvements can all be filed as issues on the project’s GitHub tracker. The project is in its early days, and feedback from external users is actively sought.

Digging Deeper with Statistics

For a closer look at YJIT’s behavior, you can build it with a debug or statistics flag:

CFLAGS='-DRUBY_DEBUG=1'
CFLAGS='-DYJIT_STATS=1'

Compiling with either flag expands the YJIT.runtime_stats output from two entries to hundreds. To get the data, run Ruby with YJIT_STATS set as an environment variable or with the yjit-stats command-line flag. At exit, Ruby then prints a report that includes the percentage of instructions executed by YJIT versus the interpreter, the amount of generated native code, and the number of ISEQs (essentially methods) that YJIT compiled.