---
title: "MJIT Support in Ruby 2.6"
description: "MJIT Support in Ruby 2.6"
canonical_url: "https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6"
markdown_url: "https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6.md"
---

# MJIT Support in Ruby 2.6

MJIT Support in Ruby 2.6

- Author: Sudeep Tarlekar
- Published: March 5, 2019
- Categories: Ruby 2.6, Ruby

### What is JIT?

JIT stands for Just-In-Time compiler. JIT converts repetitive code into bytecode
which can then be sent to the processor directly, hence, saving time by not
compiling the same piece of code over and over.

### Ruby 2.6

MJIT is introduced in Ruby 2.6. It is most commonly known as MRI JIT or Method
Based JIT.

It is a part of the Ruby 3x3 project started by Matz. The name "Ruby 3x3"
signifies Ruby 3.0 will be 3 times faster than Ruby 2.0 and it will focus mainly
on performance. In addition to performance, it also aims for the following
things:

1. Portability
2. Stability
3. Security

MJIT is still in development, therefore, MJIT is optional in Ruby 2.6. If you
are running Ruby 2.6, then you can execute the following command.

```shell
ruby --help
```

You will see following options.

```shell
--Jit-wait # Wait program execution until code compiles.
--jit-verbose=num # Level information MJIT compiler prints for Ruby program.
--jit-min-calls=num # Minimum count in loops for which MJIT should work.
--jit-max-cache
--jit-save-temps # Save compiled library to the file.
```

Vladimir Makarov proposed improving performance by replacing VM instructions
with RTL(Register Transfer Language) and introducing the Method based JIT
compiler.

Vladimir explained MJIT architecture in his
[RubyKaigi 2017 conference keynote](https://youtu.be/qpZDw-p9yag?t=1655).

Ruby's compiler converts the code to YARV(Yet Another Ruby VM) instructions and
then these instructions are run by the Ruby Virtual Machine. Code that is
executed too often is converted to RTL instructions, which runs faster.

Let's take a look at how MJIT works.

```ruby

# mjit.rb

require 'benchmark'

puts Benchmark.measure {
def test_while
start_time = Time.now
i = 0

    while i < 4
      i += 1
    end

    i
    puts Time.now - start_time

end

4.times { test_while }
}
```

Let's run this code with MJIT options and check what we got.

```shell
ruby --jit --jit-verbose=1 --jit-wait --disable-gems mjit.rb
```

```shell
Time taken is 4.0e-06
Time taken is 0.0
Time taken is 0.0
Time taken is 0.0
0.000082 0.000032 0.000114 ( 0.000105)
Successful MJIT finish
```

Nothing interesting right? And why is that? because we are iterating the loop
for 4 times and default value for MJIT to work is 5. We can always decide after
how many calls MJIT should work by providing `--jit-min-calls=#number` option.

Let's tweak the program a bit so MJIT gets to work.

```ruby
require 'benchmark'

puts Benchmark.measure {
def test_while
start_time = Time.now
i = 0

    while i < 4_00_00_000
      i += 1
    end

    puts "Time taken is #{Time.now - start_time}"

end

10.times { test_while }
}
```

After running the above code we can see some work done by MJIT.

```shell
Time taken is 0.457916
Time taken is 0.455921
Time taken is 0.454672
Time taken is 0.452823
JIT success (72.5ms): block (2 levels) in <main>@mjit.rb:15 -> /var/folders/v6/\_6sh53vn5gl3lct18w533gr80000gn/T//\_ruby_mjit_p66220u0.c
JIT success (140.9ms): test_while@mjit.rb:4 -> /var/folders/v6/\_6sh53vn5gl3lct18w533gr80000gn/T//\_ruby_mjit_p66220u1.c
JIT compaction (23.0ms): Compacted 2 methods -> /var/folders/v6/\_6sh53vn5gl3lct18w533gr80000gn/T//\_ruby_mjit_p66220u2.bundle
Time taken is 0.463703
Time taken is 0.102852
Time taken is 0.103335
Time taken is 0.103299
Time taken is 0.103252
Time taken is 0.103261
2.797843 0.005357 3.141944 ( 2.801391)
Successful MJIT finish
```

Here's what's happening. Method ran 4 times and on the 5th call it found it is
running same code again. So MJIT started a separate thread to convert the code
into RTL instructions, which created a shared object library. Next, threads took
that shared code and executed directly. As we passed option `--jit-verbose=1` we
can see what MJIT did.

What we are seeing in output is the following:

1. Time taken to compile.
2. What block of code is compiled by JIT.
3. Location of compiled code.

We can open the file and see how MJIT converted the piece of code to binary
instructions but for that we need to pass another option which is
`--jit-save-temps` and then just inspect those files.

After compiling the code to RTL instructions, take a look at the execution time.
It dropped down to 0.10 ms from 0.46 ms. That's a neat speed bump.

Here is a comparison across some of the Ruby versions for some basic operations.

![Ruby time comparison in different versions](/blog/images/images_used_in_blog/image
ruby_mjit_execution_comparison.png)

## Rails comparison on Ruby 2.5, Ruby 2.6 and Ruby 2.6 with JIT

Create a rails application with different Ruby versions and start a server. We
can start the rails server with the JIT option, as shown below.

```shell
RUBYOPT="--jit" bundle exec rails s
```

Now, we can start testing the performance on servers. We found that Ruby 2.6 is
faster than Ruby 2.5, but enabling JIT in Ruby 2.6 does not add more value to
the Rails application.

## MJIT status and future directions

- It is in an early development stage.
- Does not work on windows.
- Needs more time to mature.
- Needs more optimisations.
- MJIT can use GCC or LLVM in the future C Compilers.

## Further reading

1. [Ruby 3x3 Performance Goal](https://developers.redhat.com/blog/2018/03/22/ruby-3x3-performance-goal)
2. [The method JIT compiler for Ruby2.6](https://medium.com/@k0kubun/the-method-jit-compiler-for-ruby-2-6-388ee0989c13)
3. [Vladimir Makarov's Ruby Edition](https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch)

## Links

- [Human page](https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6)
