***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (63.9% of total 22,558,282):
Hash#[]=: 1,700,237 ( 7.5%)
String#<<: 1,494,022 ( 6.6%)
Kernel#is_a?: 1,429,926 ( 6.3%)
Regexp#match?: 1,235,064 ( 5.5%)
Kernel#respond_to?: 1,198,251 ( 5.3%)
Hash#key?: 1,087,406 ( 4.8%)
String#setbyte: 810,022 ( 3.6%)
Integer#^: 766,624 ( 3.4%)
Kernel#block_given?: 603,613 ( 2.7%)
String#==: 590,408 ( 2.6%)
String.new: 482,204 ( 2.1%)
Hash#delete: 455,288 ( 2.0%)
BasicObject#!=: 428,771 ( 1.9%)
Hash#fetch: 408,621 ( 1.8%)
String#ascii_only?: 373,915 ( 1.7%)
ObjectSpace::WeakKeyMap#[]: 287,957 ( 1.3%)
NilClass#===: 277,244 ( 1.2%)
Kernel#Array: 269,590 ( 1.2%)
Array#join: 260,047 ( 1.2%)
Array#include?: 247,816 ( 1.1%)
Top-20 not annotated C methods (64.6% of total 22,425,847):
Hash#[]=: 1,700,416 ( 7.6%)
Kernel#is_a?: 1,515,668 ( 6.8%)
String#<<: 1,494,022 ( 6.7%)
Regexp#match?: 1,235,064 ( 5.5%)
Kernel#respond_to?: 1,198,251 ( 5.3%)
Hash#key?: 1,087,406 ( 4.8%)
String#setbyte: 810,022 ( 3.6%)
Integer#^: 766,624 ( 3.4%)
Kernel#block_given?: 603,613 ( 2.7%)
String#==: 601,114 ( 2.7%)
String.new: 482,204 ( 2.2%)
Hash#delete: 455,288 ( 2.0%)
BasicObject#!=: 428,876 ( 1.9%)
Hash#fetch: 408,621 ( 1.8%)
String#ascii_only?: 373,915 ( 1.7%)
ObjectSpace::WeakKeyMap#[]: 287,957 ( 1.3%)
NilClass#===: 277,244 ( 1.2%)
Kernel#Array: 269,590 ( 1.2%)
Array#include?: 247,816 ( 1.1%)
Kernel#dup: 239,887 ( 1.1%)
Top-2 not optimized method types for send (100.0% of total 186,159):
iseq: 112,747 (60.6%)
cfunc: 73,412 (39.4%)
Top-6 not optimized method types for send_without_block (100.0% of total 8,142,248):
iseq: 3,464,671 (42.6%)
optimized: 2,632,884 (32.3%)
bmethod: 1,290,701 (15.9%)
alias: 706,020 ( 8.7%)
null: 47,942 ( 0.6%)
cfunc: 30 ( 0.0%)
Top-11 not optimized instructions (100.0% of total 8,394,873):
invokesuper: 5,602,274 (66.7%)
invokeblock: 1,764,936 (21.0%)
sendforward: 551,832 ( 6.6%)
opt_eq: 441,959 ( 5.3%)
opt_plus: 31,635 ( 0.4%)
opt_send_without_block: 1,163 ( 0.0%)
opt_lt: 372 ( 0.0%)
opt_mult: 251 ( 0.0%)
opt_ge: 193 ( 0.0%)
opt_neq: 149 ( 0.0%)
opt_or: 109 ( 0.0%)
Top-8 send fallback reasons (100.0% of total 40,619,436):
send_without_block_polymorphic: 12,933,921 (31.8%)
send_no_profiles: 9,033,636 (22.2%)
not_optimized_instruction: 8,394,873 (20.7%)
send_without_block_not_optimized_method_type: 8,142,248 (20.0%)
send_without_block_no_profiles: 1,709,913 ( 4.2%)
send_without_block_cfunc_array_variadic: 215,046 ( 0.5%)
send_not_optimized_method_type: 186,159 ( 0.5%)
obj_to_string_not_string: 3,640 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 1,604,456):
getclassvariable: 458,136 (28.6%)
getblockparam: 455,921 (28.4%)
checkkeyword: 265,425 (16.5%)
invokesuperforward: 239,383 (14.9%)
expandarray: 137,305 ( 8.6%)
getconstant: 48,100 ( 3.0%)
checkmatch: 149 ( 0.0%)
once: 23 ( 0.0%)
opt_duparray_send: 14 ( 0.0%)
Top-3 compile error reasons (100.0% of total 5,570,130):
register_spill_on_alloc: 4,994,130 (89.7%)
exception_handler: 356,784 ( 6.4%)
register_spill_on_ccall: 219,216 ( 3.9%)
Top-16 side exit reasons (100.0% of total 12,412,191):
compile_error: 5,570,130 (44.9%)
unhandled_yarv_insn: 1,604,456 (12.9%)
guard_shape_failure: 1,462,883 (11.8%)
guard_type_failure: 845,891 ( 6.8%)
block_param_proxy_not_iseq_or_ifunc: 765,968 ( 6.2%)
unhandled_kwarg: 658,341 ( 5.3%)
unhandled_splat: 446,990 ( 3.6%)
patchpoint_stable_constant_names: 397,594 ( 3.2%)
unknown_newarray_send: 332,740 ( 2.7%)
unhandled_hir_insn: 160,205 ( 1.3%)
patchpoint_method_redefined: 106,687 ( 0.9%)
block_param_proxy_modified: 59,589 ( 0.5%)
obj_to_string_fallback: 553 ( 0.0%)
patchpoint_no_ep_escape: 155 ( 0.0%)
interrupt: 8 ( 0.0%)
patchpoint_no_singleton_class: 1 ( 0.0%)
send_count: 133,391,539
dynamic_send_count: 40,619,436 (30.5%)
optimized_send_count: 92,772,103 (69.5%)
iseq_optimized_send_count: 39,936,536 (29.9%)
inline_cfunc_optimized_send_count: 30,277,285 (22.7%)
non_variadic_cfunc_optimized_send_count: 16,755,937 (12.6%)
variadic_cfunc_optimized_send_count: 5,802,345 ( 4.3%)
dynamic_getivar_count: 10,980,323
dynamic_setivar_count: 12,962,722
compiled_iseq_count: 2,530
failed_iseq_count: 245
compile_time: 402ms
profile_time: 19ms
gc_time: 33ms
invalidation_time: 4ms
vm_write_pc_count: 125,997,735
vm_write_sp_count: 122,927,105
vm_write_locals_count: 122,927,105
vm_write_stack_count: 122,927,105
vm_write_to_parent_iseq_local_count: 385,461
vm_read_from_parent_iseq_local_count: 11,266,484
code_region_bytes: 12,156,928
side_exit_count: 12,412,191
total_insn_count: 866,779,936
vm_insn_count: 216,821,024
zjit_insn_count: 649,958,912
ratio_in_zjit: 75.0%
This issue aims to make ZJIT as fast as YJIT on the following benchmarks in yjit-bench:
activerecord and erubi-rails should also be improved. But it's nice to focus on realistic, whole-application benchmarks.
Burndown list
Reduce side exits
ratio_in_zjitis still 72%. This should be the top priority until we increase it to 98-99% or more. When it's low, it not only becomes slower due to cache thrashing but also randomly misses already-implemented optimizations.We should look at "side exit reasons" and its sub-categories like "compile error reasons" and "unhandled YARV insns", and fix the ones that happen most frequently.
register_spill_on_*ZJIT: Support spilling in the register allocator #716 (@k0kubun)guard_type_failureZJIT: guard_type_failure happens too frequently on lobsters #771 (not assigned)guard_shape_failureZJIT: guard_shape_failure happens too frequently on ruby-bench #727 (not assigned)block_param_proxy_not_iseq_or_ifuncZJIT: Fix block_param_proxy_not_iseq_or_ifunc exits #772 (not assigned)Reduce dynamic sends
Method calls are a major source of potential speedups. Even if you fully materialize a frame like YJIT does, they can still be a lot faster than the interpreter (at least as fast as YJIT) by reducing memory reads and removing function calls.
We should look at "send fallback reasons" and its sub-categories like "not optimized instructions" and "not optimized method types", and fix the ones that happen most frequently.
send_without_block_cfunc_not_variadicZJIT: Support calls to non-leaf C functions #759 (@st0012)send_without_block_polymorphicZJIT: send_without_block_polymorphic happens too frequently #776 (not assigned)opt_arefrb_hash_arefcall if a profiled type is Hash #793 (not assigned)sendZJIT: Support compilingSendwithout dynamic dispatch #777 (@st0012)Reduce things slower than YJIT
There are overheads in ZJIT that YJIT doesn't have. To make ZJIT faster than YJIT, we need to remove them, or at least minimize their impact.
ZJIT stats
ZJIT stats on lobsters as of 2025-10-17
ZJIT stats on railsbench as of 2025-10-17