Some versions are slow ● Don't use Ruby 2.x ○ Ruby 3.0 has better CPU cache efficiency ● Even Ruby 3 has slow versions ○ MJIT doesn't work properly in Ruby 3.0.1 ○ Ruby 3.0.0 is OK, but others might have throttling issues
TracePoint, GC.compact, and Ractor ● MJIT can be disabled when GC.compact or TracePoint is used ○ Ruby 3.1 shows "JIT cancel" on --jit-verbose=1 when it happens ● However, Ruby 3.1 supported TracePoint :class events for Zeitwerk ● MJIT has performance issues when you have Ractors
Change --jit-max-cache for Ruby 3.0 ● The default --jit-max-cache is 100 in Ruby 3.0 ● It should be large enough to compile everything, like 10,000 ○ Use --jit-verbose=1 to see what's happening
Wait until everything is compiled ● When a C compiler is running, the interpreter becomes slower ○ We've found no workaround so far ● So be sure to see the end of compilation with --jit-verbose=1 ○ This can take some minutes
The lifecycle of JIT-ed code ● MJIT's code has multiple stages: ○ Fragmented code with full optimizations ○ Fragmented code with partial optimizations ○ Compacted code with partial optimizations ● All methods should be in the last stage to see the peak performance
JIT recompile ● MJIT disables optimizations that didn't work and recompiles the code ● Look for "JIT recompile" shown by --jit-verbose=1 ● Your log should NOT end with "MJIT recompile" to see the peak performance
JIT compaction ● Once everything is compiled, MJIT schedules "JIT compaction" ● Your --jit-verbose=1 log should end with this to see the peak performance
Why do we have multiple JITs? ● Are we competing? ○ No, we contribute to each other's project as well ● Multi-tier JIT? ○ Efficiently mixing the code of MJIT and YJIT might be hard ○ At least MJIT needs to be replaced by MIR for better control
A long-term idea ● Unblock inlining over C methods ○ YJIT cannot inline and optimize C methods as is ○ MJIT has Ruby → C inlining, but not C → Ruby yet ○ Rewrite more C methods to Ruby and/or integrate MIR