diff options
| author | Max Bernstein <rubybugs@bernsteinbear.com> | 2025-10-14 16:17:54 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-14 16:17:54 -0400 |
| commit | de9298635dc7dd212c9d80db404f20855b1426af (patch) | |
| tree | 85b51fa48ced803d906435ef14aba13694a06be5 | |
| parent | d75207d0043c56034e68c306f6dfe5e7b4f09438 (diff) | |
ZJIT: Profile opt_size, opt_length, opt_regexpmatch2 (#14837)
These bring `send_without_block_no_profiles` numbers down more.
On lobsters:
Before: send_without_block_no_profiles: 1,293,375
After: send_without_block_no_profiles: 998,724
all stats before:
```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (71.1% of total 15,575,335):
Hash#[]: 4,519,774 (29.0%)
Kernel#is_a?: 1,030,758 ( 6.6%)
String#<<: 851,929 ( 5.5%)
Hash#[]=: 742,941 ( 4.8%)
Regexp#match?: 399,889 ( 2.6%)
String#empty?: 353,775 ( 2.3%)
Hash#key?: 349,129 ( 2.2%)
String#start_with?: 334,961 ( 2.2%)
Kernel#respond_to?: 316,527 ( 2.0%)
ObjectSpace::WeakKeyMap#[]: 238,978 ( 1.5%)
TrueClass#===: 235,771 ( 1.5%)
FalseClass#===: 231,144 ( 1.5%)
Array#include?: 211,381 ( 1.4%)
Hash#fetch: 204,702 ( 1.3%)
Kernel#block_given?: 181,792 ( 1.2%)
ActiveSupport::OrderedOptions#_get: 181,272 ( 1.2%)
Kernel#dup: 179,340 ( 1.2%)
BasicObject#!=: 175,997 ( 1.1%)
Class#new: 168,078 ( 1.1%)
Kernel#kind_of?: 165,600 ( 1.1%)
Top-20 not annotated C methods (71.6% of total 15,737,478):
Hash#[]: 4,519,784 (28.7%)
Kernel#is_a?: 1,212,649 ( 7.7%)
String#<<: 851,929 ( 5.4%)
Hash#[]=: 743,120 ( 4.7%)
Regexp#match?: 399,889 ( 2.5%)
String#empty?: 361,013 ( 2.3%)
Hash#key?: 349,129 ( 2.2%)
String#start_with?: 334,961 ( 2.1%)
Kernel#respond_to?: 316,527 ( 2.0%)
ObjectSpace::WeakKeyMap#[]: 238,978 ( 1.5%)
TrueClass#===: 235,771 ( 1.5%)
FalseClass#===: 231,144 ( 1.5%)
Array#include?: 211,381 ( 1.3%)
Hash#fetch: 204,702 ( 1.3%)
Kernel#block_given?: 191,661 ( 1.2%)
ActiveSupport::OrderedOptions#_get: 181,272 ( 1.2%)
Kernel#dup: 179,347 ( 1.1%)
BasicObject#!=: 176,181 ( 1.1%)
Class#new: 168,078 ( 1.1%)
Kernel#kind_of?: 165,634 ( 1.1%)
Top-2 not optimized method types for send (100.0% of total 72,318):
cfunc: 48,055 (66.4%)
iseq: 24,263 (33.6%)
Top-6 not optimized method types for send_without_block (100.0% of total 4,523,648):
iseq: 2,271,904 (50.2%)
bmethod: 985,636 (21.8%)
optimized: 949,702 (21.0%)
alias: 310,746 ( 6.9%)
null: 5,106 ( 0.1%)
cfunc: 554 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 4,293,096):
invokesuper: 2,373,391 (55.3%)
invokeblock: 811,872 (18.9%)
sendforward: 505,448 (11.8%)
opt_eq: 451,754 (10.5%)
opt_plus: 74,403 ( 1.7%)
opt_minus: 36,225 ( 0.8%)
opt_send_without_block: 21,792 ( 0.5%)
opt_neq: 7,231 ( 0.2%)
opt_mult: 6,752 ( 0.2%)
opt_or: 3,753 ( 0.1%)
opt_lt: 348 ( 0.0%)
opt_ge: 91 ( 0.0%)
opt_gt: 36 ( 0.0%)
Top-9 send fallback reasons (100.0% of total 25,824,463):
send_without_block_polymorphic: 9,721,727 (37.6%)
send_no_profiles: 5,894,760 (22.8%)
send_without_block_not_optimized_method_type: 4,523,648 (17.5%)
not_optimized_instruction: 4,293,096 (16.6%)
send_without_block_no_profiles: 1,293,386 ( 5.0%)
send_not_optimized_method_type: 72,318 ( 0.3%)
send_without_block_cfunc_array_variadic: 15,134 ( 0.1%)
obj_to_string_not_string: 9,765 ( 0.0%)
send_without_block_direct_too_many_args: 629 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 690,482):
expandarray: 328,490 (47.6%)
checkkeyword: 190,694 (27.6%)
getclassvariable: 59,901 ( 8.7%)
invokesuperforward: 49,503 ( 7.2%)
getblockparam: 48,651 ( 7.0%)
opt_duparray_send: 11,978 ( 1.7%)
getconstant: 952 ( 0.1%)
checkmatch: 290 ( 0.0%)
once: 23 ( 0.0%)
Top-3 compile error reasons (100.0% of total 3,752,502):
register_spill_on_alloc: 3,457,791 (92.1%)
register_spill_on_ccall: 176,348 ( 4.7%)
exception_handler: 118,363 ( 3.2%)
Top-14 side exit reasons (100.0% of total 10,860,787):
compile_error: 3,752,502 (34.6%)
guard_type_failure: 2,638,903 (24.3%)
guard_shape_failure: 1,917,195 (17.7%)
unhandled_yarv_insn: 690,482 ( 6.4%)
block_param_proxy_not_iseq_or_ifunc: 535,787 ( 4.9%)
unhandled_kwarg: 421,943 ( 3.9%)
patchpoint: 370,449 ( 3.4%)
unknown_newarray_send: 314,785 ( 2.9%)
unhandled_splat: 122,060 ( 1.1%)
unhandled_hir_insn: 76,396 ( 0.7%)
block_param_proxy_modified: 19,193 ( 0.2%)
obj_to_string_fallback: 566 ( 0.0%)
interrupt: 504 ( 0.0%)
guard_type_not_failure: 22 ( 0.0%)
send_count: 66,945,801
dynamic_send_count: 25,824,463 (38.6%)
optimized_send_count: 41,121,338 (61.4%)
iseq_optimized_send_count: 18,587,368 (27.8%)
inline_cfunc_optimized_send_count: 6,958,635 (10.4%)
non_variadic_cfunc_optimized_send_count: 12,911,155 (19.3%)
variadic_cfunc_optimized_send_count: 2,664,180 ( 4.0%)
dynamic_getivar_count: 7,365,975
dynamic_setivar_count: 7,245,897
compiled_iseq_count: 4,794
failed_iseq_count: 450
compile_time: 760ms
profile_time: 9ms
gc_time: 8ms
invalidation_time: 55ms
vm_write_pc_count: 64,284,053
vm_write_sp_count: 62,940,297
vm_write_locals_count: 62,940,297
vm_write_stack_count: 62,940,297
vm_write_to_parent_iseq_local_count: 292,446
vm_read_from_parent_iseq_local_count: 6,470,923
code_region_bytes: 23,019,520
side_exit_count: 10,860,787
total_insn_count: 517,576,320
vm_insn_count: 163,188,910
zjit_insn_count: 354,387,410
ratio_in_zjit: 68.5%
```
all stats after:
```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (70.4% of total 15,740,856):
Hash#[]: 4,519,792 (28.7%)
Kernel#is_a?: 1,030,776 ( 6.5%)
String#<<: 851,940 ( 5.4%)
Hash#[]=: 742,914 ( 4.7%)
Regexp#match?: 399,887 ( 2.5%)
String#empty?: 353,775 ( 2.2%)
Hash#key?: 349,139 ( 2.2%)
String#start_with?: 334,961 ( 2.1%)
Kernel#respond_to?: 316,529 ( 2.0%)
ObjectSpace::WeakKeyMap#[]: 238,978 ( 1.5%)
TrueClass#===: 235,771 ( 1.5%)
FalseClass#===: 231,144 ( 1.5%)
Array#include?: 211,381 ( 1.3%)
Hash#fetch: 204,702 ( 1.3%)
Kernel#block_given?: 181,788 ( 1.2%)
ActiveSupport::OrderedOptions#_get: 181,272 ( 1.2%)
Kernel#dup: 179,341 ( 1.1%)
BasicObject#!=: 175,996 ( 1.1%)
Class#new: 168,079 ( 1.1%)
Kernel#kind_of?: 165,600 ( 1.1%)
Top-20 not annotated C methods (70.9% of total 15,902,999):
Hash#[]: 4,519,802 (28.4%)
Kernel#is_a?: 1,212,667 ( 7.6%)
String#<<: 851,940 ( 5.4%)
Hash#[]=: 743,093 ( 4.7%)
Regexp#match?: 399,887 ( 2.5%)
String#empty?: 361,013 ( 2.3%)
Hash#key?: 349,139 ( 2.2%)
String#start_with?: 334,961 ( 2.1%)
Kernel#respond_to?: 316,529 ( 2.0%)
ObjectSpace::WeakKeyMap#[]: 238,978 ( 1.5%)
TrueClass#===: 235,771 ( 1.5%)
FalseClass#===: 231,144 ( 1.5%)
Array#include?: 211,381 ( 1.3%)
Hash#fetch: 204,702 ( 1.3%)
Kernel#block_given?: 191,657 ( 1.2%)
ActiveSupport::OrderedOptions#_get: 181,272 ( 1.1%)
Kernel#dup: 179,348 ( 1.1%)
BasicObject#!=: 176,180 ( 1.1%)
Class#new: 168,079 ( 1.1%)
Kernel#kind_of?: 165,634 ( 1.0%)
Top-2 not optimized method types for send (100.0% of total 72,318):
cfunc: 48,055 (66.4%)
iseq: 24,263 (33.6%)
Top-6 not optimized method types for send_without_block (100.0% of total 4,523,637):
iseq: 2,271,900 (50.2%)
bmethod: 985,636 (21.8%)
optimized: 949,695 (21.0%)
alias: 310,746 ( 6.9%)
null: 5,106 ( 0.1%)
cfunc: 554 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 4,293,128):
invokesuper: 2,373,401 (55.3%)
invokeblock: 811,890 (18.9%)
sendforward: 505,449 (11.8%)
opt_eq: 451,754 (10.5%)
opt_plus: 74,403 ( 1.7%)
opt_minus: 36,228 ( 0.8%)
opt_send_without_block: 21,792 ( 0.5%)
opt_neq: 7,231 ( 0.2%)
opt_mult: 6,752 ( 0.2%)
opt_or: 3,753 ( 0.1%)
opt_lt: 348 ( 0.0%)
opt_ge: 91 ( 0.0%)
opt_gt: 36 ( 0.0%)
Top-9 send fallback reasons (100.0% of total 25,530,605):
send_without_block_polymorphic: 9,722,499 (38.1%)
send_no_profiles: 5,894,763 (23.1%)
send_without_block_not_optimized_method_type: 4,523,637 (17.7%)
not_optimized_instruction: 4,293,128 (16.8%)
send_without_block_no_profiles: 998,732 ( 3.9%)
send_not_optimized_method_type: 72,318 ( 0.3%)
send_without_block_cfunc_array_variadic: 15,134 ( 0.1%)
obj_to_string_not_string: 9,765 ( 0.0%)
send_without_block_direct_too_many_args: 629 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 690,482):
expandarray: 328,490 (47.6%)
checkkeyword: 190,694 (27.6%)
getclassvariable: 59,901 ( 8.7%)
invokesuperforward: 49,503 ( 7.2%)
getblockparam: 48,651 ( 7.0%)
opt_duparray_send: 11,978 ( 1.7%)
getconstant: 952 ( 0.1%)
checkmatch: 290 ( 0.0%)
once: 23 ( 0.0%)
Top-3 compile error reasons (100.0% of total 3,752,500):
register_spill_on_alloc: 3,457,792 (92.1%)
register_spill_on_ccall: 176,348 ( 4.7%)
exception_handler: 118,360 ( 3.2%)
Top-14 side exit reasons (100.0% of total 10,860,797):
compile_error: 3,752,500 (34.6%)
guard_type_failure: 2,638,909 (24.3%)
guard_shape_failure: 1,917,203 (17.7%)
unhandled_yarv_insn: 690,482 ( 6.4%)
block_param_proxy_not_iseq_or_ifunc: 535,784 ( 4.9%)
unhandled_kwarg: 421,947 ( 3.9%)
patchpoint: 370,474 ( 3.4%)
unknown_newarray_send: 314,786 ( 2.9%)
unhandled_splat: 122,067 ( 1.1%)
unhandled_hir_insn: 76,395 ( 0.7%)
block_param_proxy_modified: 19,193 ( 0.2%)
obj_to_string_fallback: 566 ( 0.0%)
interrupt: 469 ( 0.0%)
guard_type_not_failure: 22 ( 0.0%)
send_count: 66,945,326
dynamic_send_count: 25,530,605 (38.1%)
optimized_send_count: 41,414,721 (61.9%)
iseq_optimized_send_count: 18,587,439 (27.8%)
inline_cfunc_optimized_send_count: 7,086,426 (10.6%)
non_variadic_cfunc_optimized_send_count: 13,076,682 (19.5%)
variadic_cfunc_optimized_send_count: 2,664,174 ( 4.0%)
dynamic_getivar_count: 7,365,985
dynamic_setivar_count: 7,245,954
compiled_iseq_count: 4,794
failed_iseq_count: 450
compile_time: 748ms
profile_time: 9ms
gc_time: 8ms
invalidation_time: 58ms
vm_write_pc_count: 64,155,801
vm_write_sp_count: 62,812,041
vm_write_locals_count: 62,812,041
vm_write_stack_count: 62,812,041
vm_write_to_parent_iseq_local_count: 292,448
vm_read_from_parent_iseq_local_count: 6,470,939
code_region_bytes: 23,052,288
side_exit_count: 10,860,797
total_insn_count: 517,576,915
vm_insn_count: 163,192,099
zjit_insn_count: 354,384,816
ratio_in_zjit: 68.5%
```
| -rw-r--r-- | insns.def | 3 | ||||
| -rw-r--r-- | zjit/src/cruby_bindings.inc.rs | 9 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 85 | ||||
| -rw-r--r-- | zjit/src/profile.rs | 3 |
4 files changed, 96 insertions, 4 deletions
@@ -1553,6 +1553,7 @@ opt_length (CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { val = vm_opt_length(recv, BOP_LENGTH); @@ -1567,6 +1568,7 @@ opt_size (CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { val = vm_opt_length(recv, BOP_SIZE); @@ -1626,6 +1628,7 @@ opt_regexpmatch2 (VALUE obj2, VALUE obj1) (VALUE val) // attr bool leaf = false; /* match_at() has rb_thread_check_ints() */ +// attr bool zjit_profile = true; { val = vm_opt_regexpmatch2(obj2, obj1); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 6e3ae05194..56b569e064 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -699,9 +699,12 @@ pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 234; pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 235; pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 236; pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 239; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 242; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 243; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 022451e8ab..33c034b819 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4957,7 +4957,7 @@ mod tests { } #[track_caller] - fn assert_contains_opcode(method: &str, opcode: u32) { + pub fn assert_contains_opcode(method: &str, opcode: u32) { let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; assert!(iseq_contains_opcode(iseq, opcode), "iseq {method} does not contain {}", insn_name(opcode as usize)); @@ -7999,6 +7999,7 @@ mod opt_tests { use super::*; use crate::{hir_strings, options::*}; use insta::assert_snapshot; + use super::tests::assert_contains_opcode; #[track_caller] fn hir_string(method: &str) -> String { @@ -12876,4 +12877,86 @@ mod opt_tests { Return v27 "); } + + #[test] + fn test_optimize_array_length() { + eval(" + def test(arr) = arr.length + test([]) + "); + assert_contains_opcode("test", YARVINSN_opt_length); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + v26:Fixnum = CCall length@0x1038, v25 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_optimize_array_size() { + eval(" + def test(arr) = arr.size + test([]) + "); + assert_contains_opcode("test", YARVINSN_opt_size); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + v26:Fixnum = CCall size@0x1038, v25 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_optimize_regexpmatch2() { + eval(r#" + def test(s) = s =~ /a/ + test("foo") + "#); + assert_contains_opcode("test", YARVINSN_opt_regexpmatch2); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v13:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) + PatchPoint NoSingletonClass(String@0x1008) + v26:StringExact = GuardType v9, StringExact + v27:BasicObject = CCallWithFrame =~@0x1040, v26, v13 + CheckInterrupts + Return v27 + "); + } } diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index c2c35f687b..e7db47142b 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -78,7 +78,10 @@ fn profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) { YARVINSN_opt_aset => profile_operands(profiler, profile, 3), YARVINSN_opt_not => profile_operands(profiler, profile, 1), YARVINSN_getinstancevariable => profile_self(profiler, profile), + YARVINSN_opt_regexpmatch2 => profile_operands(profiler, profile, 2), YARVINSN_objtostring => profile_operands(profiler, profile, 1), + YARVINSN_opt_length => profile_operands(profiler, profile, 1), + YARVINSN_opt_size => profile_operands(profiler, profile, 1), YARVINSN_opt_send_without_block => { let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); let argc = unsafe { vm_ci_argc((*cd).ci) }; |
