VM execution
By default, Ruby compiles source code into instruction sequences (YARV bytecode) that are executed by the Ruby VM. This interpretation happens for all code initially - Ruby parses source code, generates the instruction sequences and VM executes them directly.
At some point, JIT compilation occurs when code execution becomes “hot” (executed frequently to warrant optimization). How is this determined?
MJIT (Ruby 2.6)
Triggered after a method gets called 10,000 times.
YJIT compilation thresholds (Ruby 3.1+)
YJIT uses a dynamic threshold system. Methods that exceed call the count threshold will get targeted by the compiler. Once “JIT compiled”, subsequent calls to that method will execute the optimized native machine code instead of intepreting the instruction sequences.
- Small Call Threshold: 30 calls
- Large Call Threshold: 120 calls
- Cold Threshold: 200,000 calls
YJIT switches between small and large thresholds based on application size - applications with more than 40,000 ISEQs are considered “large” and use the 120-call threshold yjit/src/options.rs. The cold threshold prevents compilation of methods that are called infrequently.
ZJIT compilation thresholds upstreamed
In contrast, ZJIT focuses on profiling before compilation. zjit/src/options.rs.
The profile threshold determines when to start profiling instruction sequences, while the call threshold determines when to compile. How it’s done: The profiling process replaces regular ISEQ with special zjit versions that collect runtime information. Once these profiled ISEQ reach call thresholds(2 calls by default), ZJIT compiles the ISEQ and then performs cleanup/disables the profiling.
See also: Virtual Machine