summaryrefslogtreecommitdiff
path: root/test/ruby
AgeCommit message (Collapse)Author
28 hoursPrevent starvation when acquiring mutex over and over (#15877)Luke Gruber
Continually locking a mutex m can lead to starvation if all other threads are on the waitq of m. See https://bugs.ruby-lang.org/issues/21840 for more details. Solution: When a thread `T1` wakes up `T2` during mutex unlock but `T1` or any other thread successfully acquires it before `T2`, then we record the `running_time` of the thread during mutex acquisition. Then during unlock, if that thread's running_time is less than the saved running time, we set it back to the saved time. Fixes [Bug #21840]
32 hoursFix RUBY_MN_THREADS sleep returning prematurely (#15868)Chris Hasiński
timer_thread_check_exceed() was returning true when the remaining time was less than 1ms, treating it as "too short time". This caused sub-millisecond sleeps (like sleep(0.0001)) to return immediately instead of actually sleeping. The fix removes this optimization that was incorrectly short-circuiting short sleep durations. Now the timeout is only considered exceeded when the actual deadline has passed. Note: There's still a separate performance issue where MN_THREADS mode is slower for sub-millisecond sleeps due to the timer thread using millisecond-resolution polling. This will require a separate fix to use sub-millisecond timeouts in kqueue/epoll. [Bug #21836]
5 daysRemove unused variable warningÉtienne Barrié
6 daysZJIT: Compile getblockparam (#15896)Nozomi Hijikata
Closes: https://github.com/Shopify/ruby/issues/863 Compile `getblockparam` insn to `GetBlockParam` HIR so that we can handle it in ZJIT. ## Benchmark ### lobsters <details> <summary>before patch</summary> ``` Average of last 10, non-warmup iters: 778ms ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (58.4% of total 16,091,748): Hash#fetch: 3,237,974 (20.1%) Regexp#match?: 708,838 ( 4.4%) Hash#key?: 702,565 ( 4.4%) String#sub!: 489,843 ( 3.0%) Set#include?: 402,395 ( 2.5%) String#<<: 396,364 ( 2.5%) String#start_with?: 379,338 ( 2.4%) Hash#delete: 331,679 ( 2.1%) String.new: 308,268 ( 1.9%) Integer#===: 279,074 ( 1.7%) Symbol#end_with?: 255,538 ( 1.6%) Kernel#is_a?: 250,000 ( 1.6%) Process.clock_gettime: 221,598 ( 1.4%) Integer#>: 219,718 ( 1.4%) String#match?: 218,057 ( 1.4%) String#downcase: 213,127 ( 1.3%) Integer#<=: 202,617 ( 1.3%) Time#to_i: 195,248 ( 1.2%) Time#subsec: 192,277 ( 1.2%) Time#utc?: 188,500 ( 1.2%) Top-20 calls to C functions from JIT code (83.4% of total 126,501,142): rb_vm_opt_send_without_block: 35,338,443 (27.9%) rb_vm_send: 10,126,272 ( 8.0%) rb_hash_aref: 9,221,146 ( 7.3%) rb_vm_env_write: 8,615,394 ( 6.8%) rb_zjit_writebarrier_check_immediate: 7,666,061 ( 6.1%) rb_vm_getinstancevariable: 5,902,473 ( 4.7%) rb_ivar_get_at_no_ractor_check: 4,775,750 ( 3.8%) rb_obj_is_kind_of: 3,718,303 ( 2.9%) rb_vm_invokesuper: 2,705,394 ( 2.1%) rb_hash_aset: 2,422,892 ( 1.9%) rb_vm_setinstancevariable: 2,385,262 ( 1.9%) rb_vm_opt_getconstant_path: 2,321,875 ( 1.8%) Hash#fetch: 1,819,675 ( 1.4%) fetch: 1,418,299 ( 1.1%) rb_vm_invokeblock: 1,387,466 ( 1.1%) rb_str_buf_append: 1,378,634 ( 1.1%) rb_ec_ary_new_from_values: 1,338,599 ( 1.1%) rb_class_allocate_instance: 1,300,827 ( 1.0%) rb_hash_new_with_size: 906,352 ( 0.7%) rb_vm_sendforward: 799,626 ( 0.6%) Top-2 not optimized method types for send (100.0% of total 5,166,211): iseq: 5,163,389 (99.9%) null: 2,822 ( 0.1%) Top-3 not optimized method types for send_without_block (100.0% of total 526,119): optimized_send: 479,643 (91.2%) null: 42,176 ( 8.0%) optimized_block_call: 4,300 ( 0.8%) Top-3 not optimized method types for super (100.0% of total 2,365,999): cfunc: 2,251,438 (95.2%) alias: 111,257 ( 4.7%) attrset: 3,304 ( 0.1%) Top-3 instructions with uncategorized fallback reason (100.0% of total 2,214,821): invokeblock: 1,387,466 (62.6%) sendforward: 799,626 (36.1%) opt_send_without_block: 27,729 ( 1.3%) Top-20 send fallback reasons (100.0% of total 50,357,201): send_without_block_polymorphic: 18,307,466 (36.4%) singleton_class_seen: 9,310,336 (18.5%) send_not_optimized_method_type: 5,166,211 (10.3%) send_without_block_no_profiles: 4,756,165 ( 9.4%) one_or_more_complex_arg_pass: 2,906,412 ( 5.8%) send_no_profiles: 2,864,323 ( 5.7%) super_not_optimized_method_type: 2,365,999 ( 4.7%) uncategorized: 2,214,821 ( 4.4%) send_without_block_megamorphic: 581,552 ( 1.2%) send_without_block_not_optimized_method_type_optimized: 483,943 ( 1.0%) send_without_block_not_optimized_need_permission: 390,364 ( 0.8%) send_polymorphic: 329,064 ( 0.7%) too_many_args_for_lir: 173,570 ( 0.3%) super_target_complex_args_pass: 131,841 ( 0.3%) super_complex_args_pass: 111,056 ( 0.2%) super_polymorphic: 86,986 ( 0.2%) argc_param_mismatch: 48,546 ( 0.1%) send_without_block_not_optimized_method_type: 42,176 ( 0.1%) send_without_block_direct_keyword_mismatch: 37,484 ( 0.1%) obj_to_string_not_string: 34,865 ( 0.1%) Top-4 setivar fallback reasons (100.0% of total 2,385,262): not_monomorphic: 2,162,525 (90.7%) not_t_object: 125,178 ( 5.2%) too_complex: 97,538 ( 4.1%) new_shape_needs_extension: 21 ( 0.0%) Top-2 getivar fallback reasons (100.0% of total 6,027,586): not_monomorphic: 5,776,418 (95.8%) too_complex: 251,168 ( 4.2%) Top-3 definedivar fallback reasons (100.0% of total 406,027): not_monomorphic: 397,876 (98.0%) too_complex: 5,122 ( 1.3%) not_t_object: 3,029 ( 0.7%) Top-6 invokeblock handler (100.0% of total 1,387,466): monomorphic_iseq: 700,051 (50.5%) polymorphic: 513,455 (37.0%) monomorphic_other: 106,268 ( 7.7%) monomorphic_ifunc: 55,505 ( 4.0%) megamorphic: 6,762 ( 0.5%) no_profiles: 5,425 ( 0.4%) Top-9 popular complex argument-parameter features not optimized (100.0% of total 3,353,961): param_kw_opt: 1,408,663 (42.0%) param_forwardable: 697,209 (20.8%) param_block: 632,488 (18.9%) param_rest: 346,363 (10.3%) param_kwrest: 139,856 ( 4.2%) caller_kw_splat: 79,861 ( 2.4%) caller_splat: 43,585 ( 1.3%) caller_blockarg: 5,826 ( 0.2%) caller_kwarg: 110 ( 0.0%) Top-1 compile error reasons (100.0% of total 188,362): exception_handler: 188,362 (100.0%) Top-7 unhandled YARV insns (100.0% of total 184,408): getblockparam: 95,129 (51.6%) invokesuperforward: 81,668 (44.3%) getconstant: 3,318 ( 1.8%) setblockparam: 2,837 ( 1.5%) checkmatch: 929 ( 0.5%) expandarray: 360 ( 0.2%) once: 167 ( 0.1%) Top-3 unhandled HIR insns (100.0% of total 237,876): throw: 199,380 (83.8%) invokebuiltin: 35,775 (15.0%) array_max: 2,721 ( 1.1%) Top-20 side exit reasons (100.0% of total 15,592,861): guard_type_failure: 6,993,070 (44.8%) guard_shape_failure: 6,862,785 (44.0%) block_param_proxy_not_iseq_or_ifunc: 1,006,781 ( 6.5%) unhandled_hir_insn: 237,876 ( 1.5%) compile_error: 188,362 ( 1.2%) unhandled_yarv_insn: 184,408 ( 1.2%) block_param_proxy_modified: 29,130 ( 0.2%) patchpoint_stable_constant_names: 22,145 ( 0.1%) unhandled_newarray_send_pack: 14,481 ( 0.1%) unhandled_block_arg: 13,788 ( 0.1%) fixnum_mult_overflow: 10,866 ( 0.1%) fixnum_lshift_overflow: 10,085 ( 0.1%) patchpoint_no_ep_escape: 7,815 ( 0.1%) expandarray_failure: 4,533 ( 0.0%) guard_super_method_entry: 4,475 ( 0.0%) patchpoint_method_redefined: 1,212 ( 0.0%) patchpoint_no_singleton_class: 423 ( 0.0%) obj_to_string_fallback: 330 ( 0.0%) guard_less_failure: 163 ( 0.0%) interrupt: 114 ( 0.0%) send_count: 152,442,683 dynamic_send_count: 50,357,201 (33.0%) optimized_send_count: 102,085,482 (67.0%) dynamic_setivar_count: 2,385,262 ( 1.6%) dynamic_getivar_count: 6,027,586 ( 4.0%) dynamic_definedivar_count: 406,027 ( 0.3%) iseq_optimized_send_count: 39,671,621 (26.0%) inline_cfunc_optimized_send_count: 42,053,762 (27.6%) inline_iseq_optimized_send_count: 3,462,562 ( 2.3%) non_variadic_cfunc_optimized_send_count: 9,195,248 ( 6.0%) variadic_cfunc_optimized_send_count: 7,702,289 ( 5.1%) compiled_iseq_count: 5,552 failed_iseq_count: 0 compile_time: 1,926ms profile_time: 20ms gc_time: 27ms invalidation_time: 531ms vm_write_pc_count: 132,750,117 vm_write_sp_count: 132,750,117 vm_write_locals_count: 128,780,465 vm_write_stack_count: 128,780,465 vm_write_to_parent_iseq_local_count: 694,799 vm_read_from_parent_iseq_local_count: 14,812,747 guard_type_count: 159,813,452 guard_type_exit_ratio: 4.4% guard_shape_count: 0 code_region_bytes: 29,425,664 zjit_alloc_bytes: 44,592,776 total_mem_bytes: 74,018,440 side_exit_count: 15,592,861 total_insn_count: 938,453,078 vm_insn_count: 167,693,539 zjit_insn_count: 770,759,539 ratio_in_zjit: 82.1% ``` </details> <details> <summary>after patch</summary> ``` Average of last 10, non-warmup iters: 725ms ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (58.2% of total 16,004,664): Hash#fetch: 3,185,115 (19.9%) Regexp#match?: 708,806 ( 4.4%) Hash#key?: 702,551 ( 4.4%) String#sub!: 489,841 ( 3.1%) Set#include?: 396,625 ( 2.5%) String#<<: 396,279 ( 2.5%) String#start_with?: 379,337 ( 2.4%) Hash#delete: 331,667 ( 2.1%) String.new: 307,248 ( 1.9%) Integer#===: 279,054 ( 1.7%) Symbol#end_with?: 255,538 ( 1.6%) Kernel#is_a?: 246,961 ( 1.5%) Process.clock_gettime: 221,588 ( 1.4%) Integer#>: 219,718 ( 1.4%) String#match?: 218,059 ( 1.4%) String#downcase: 213,109 ( 1.3%) Integer#<=: 202,617 ( 1.3%) Time#to_i: 192,211 ( 1.2%) Time#subsec: 189,240 ( 1.2%) String#to_sym: 185,947 ( 1.2%) Top-20 calls to C functions from JIT code (83.4% of total 126,772,007): rb_vm_opt_send_without_block: 35,829,863 (28.3%) rb_vm_send: 10,108,894 ( 8.0%) rb_hash_aref: 9,009,231 ( 7.1%) rb_vm_env_write: 8,571,665 ( 6.8%) rb_zjit_writebarrier_check_immediate: 7,702,599 ( 6.1%) rb_vm_getinstancevariable: 5,930,325 ( 4.7%) rb_ivar_get_at_no_ractor_check: 4,764,439 ( 3.8%) rb_obj_is_kind_of: 3,722,865 ( 2.9%) rb_vm_invokesuper: 2,687,484 ( 2.1%) rb_hash_aset: 2,421,186 ( 1.9%) rb_vm_setinstancevariable: 2,355,461 ( 1.9%) rb_vm_opt_getconstant_path: 2,295,528 ( 1.8%) Hash#fetch: 1,779,524 ( 1.4%) fetch: 1,405,591 ( 1.1%) rb_vm_invokeblock: 1,385,989 ( 1.1%) rb_str_buf_append: 1,369,177 ( 1.1%) rb_ec_ary_new_from_values: 1,337,865 ( 1.1%) rb_class_allocate_instance: 1,295,755 ( 1.0%) rb_hash_new_with_size: 902,684 ( 0.7%) rb_vm_sendforward: 798,572 ( 0.6%) Top-2 not optimized method types for send (100.0% of total 4,902,716): iseq: 4,899,894 (99.9%) null: 2,822 ( 0.1%) Top-3 not optimized method types for send_without_block (100.0% of total 526,064): optimized_send: 479,589 (91.2%) null: 42,176 ( 8.0%) optimized_block_call: 4,299 ( 0.8%) Top-3 not optimized method types for super (100.0% of total 2,350,245): cfunc: 2,239,567 (95.3%) alias: 107,374 ( 4.6%) attrset: 3,304 ( 0.1%) Top-3 instructions with uncategorized fallback reason (100.0% of total 2,216,683): invokeblock: 1,385,989 (62.5%) sendforward: 798,572 (36.0%) opt_send_without_block: 32,122 ( 1.4%) Top-20 send fallback reasons (99.9% of total 50,810,802): send_without_block_polymorphic: 18,668,686 (36.7%) singleton_class_seen: 9,323,039 (18.3%) send_not_optimized_method_type: 4,902,716 ( 9.6%) send_without_block_no_profiles: 4,824,297 ( 9.5%) send_no_profiles: 2,853,944 ( 5.6%) one_or_more_complex_arg_pass: 2,829,717 ( 5.6%) super_not_optimized_method_type: 2,350,245 ( 4.6%) uncategorized: 2,216,683 ( 4.4%) send_without_block_megamorphic: 723,037 ( 1.4%) send_polymorphic: 544,026 ( 1.1%) send_without_block_not_optimized_method_type_optimized: 483,888 ( 1.0%) send_without_block_not_optimized_need_permission: 390,364 ( 0.8%) too_many_args_for_lir: 172,809 ( 0.3%) super_target_complex_args_pass: 128,824 ( 0.3%) super_complex_args_pass: 111,053 ( 0.2%) super_polymorphic: 87,851 ( 0.2%) argc_param_mismatch: 50,382 ( 0.1%) send_without_block_not_optimized_method_type: 42,176 ( 0.1%) obj_to_string_not_string: 34,861 ( 0.1%) send_without_block_direct_keyword_mismatch: 32,436 ( 0.1%) Top-4 setivar fallback reasons (100.0% of total 2,355,461): not_monomorphic: 2,132,746 (90.5%) not_t_object: 125,163 ( 5.3%) too_complex: 97,531 ( 4.1%) new_shape_needs_extension: 21 ( 0.0%) Top-2 getivar fallback reasons (100.0% of total 6,055,438): not_monomorphic: 5,806,179 (95.9%) too_complex: 249,259 ( 4.1%) Top-3 definedivar fallback reasons (100.0% of total 405,302): not_monomorphic: 397,150 (98.0%) too_complex: 5,122 ( 1.3%) not_t_object: 3,030 ( 0.7%) Top-6 invokeblock handler (100.0% of total 1,385,989): monomorphic_iseq: 688,167 (49.7%) polymorphic: 523,864 (37.8%) monomorphic_other: 106,268 ( 7.7%) monomorphic_ifunc: 55,505 ( 4.0%) megamorphic: 6,761 ( 0.5%) no_profiles: 5,424 ( 0.4%) Top-9 popular complex argument-parameter features not optimized (100.0% of total 3,234,958): param_kw_opt: 1,381,881 (42.7%) param_forwardable: 685,939 (21.2%) param_block: 640,948 (19.8%) param_rest: 327,046 (10.1%) param_kwrest: 120,209 ( 3.7%) caller_kw_splat: 38,970 ( 1.2%) caller_splat: 34,029 ( 1.1%) caller_blockarg: 5,826 ( 0.2%) caller_kwarg: 110 ( 0.0%) Top-1 compile error reasons (100.0% of total 187,347): exception_handler: 187,347 (100.0%) Top-6 unhandled YARV insns (100.0% of total 89,278): invokesuperforward: 81,667 (91.5%) getconstant: 3,318 ( 3.7%) setblockparam: 2,837 ( 3.2%) checkmatch: 929 ( 1.0%) expandarray: 360 ( 0.4%) once: 167 ( 0.2%) Top-3 unhandled HIR insns (100.0% of total 236,977): throw: 198,481 (83.8%) invokebuiltin: 35,775 (15.1%) array_max: 2,721 ( 1.1%) Top-20 side exit reasons (100.0% of total 15,458,443): guard_type_failure: 6,918,397 (44.8%) guard_shape_failure: 6,859,686 (44.4%) block_param_proxy_not_iseq_or_ifunc: 1,008,346 ( 6.5%) unhandled_hir_insn: 236,977 ( 1.5%) compile_error: 187,347 ( 1.2%) unhandled_yarv_insn: 89,278 ( 0.6%) fixnum_mult_overflow: 50,739 ( 0.3%) block_param_proxy_modified: 28,119 ( 0.2%) patchpoint_stable_constant_names: 22,145 ( 0.1%) unhandled_newarray_send_pack: 14,481 ( 0.1%) unhandled_block_arg: 13,787 ( 0.1%) fixnum_lshift_overflow: 10,085 ( 0.1%) patchpoint_no_ep_escape: 7,815 ( 0.1%) expandarray_failure: 4,533 ( 0.0%) guard_super_method_entry: 4,475 ( 0.0%) patchpoint_method_redefined: 1,212 ( 0.0%) patchpoint_no_singleton_class: 423 ( 0.0%) obj_to_string_fallback: 330 ( 0.0%) guard_less_failure: 163 ( 0.0%) interrupt: 86 ( 0.0%) send_count: 151,889,096 dynamic_send_count: 50,810,802 (33.5%) optimized_send_count: 101,078,294 (66.5%) dynamic_setivar_count: 2,355,461 ( 1.6%) dynamic_getivar_count: 6,055,438 ( 4.0%) dynamic_definedivar_count: 405,302 ( 0.3%) iseq_optimized_send_count: 39,470,508 (26.0%) inline_cfunc_optimized_send_count: 41,381,565 (27.2%) inline_iseq_optimized_send_count: 3,370,961 ( 2.2%) non_variadic_cfunc_optimized_send_count: 9,210,651 ( 6.1%) variadic_cfunc_optimized_send_count: 7,644,609 ( 5.0%) compiled_iseq_count: 5,552 failed_iseq_count: 0 compile_time: 1,809ms profile_time: 15ms gc_time: 21ms invalidation_time: 526ms vm_write_pc_count: 132,774,559 vm_write_sp_count: 132,774,559 vm_write_locals_count: 128,748,998 vm_write_stack_count: 128,748,998 vm_write_to_parent_iseq_local_count: 693,262 vm_read_from_parent_iseq_local_count: 14,737,431 guard_type_count: 158,811,089 guard_type_exit_ratio: 4.4% guard_shape_count: 0 code_region_bytes: 29,458,432 zjit_alloc_bytes: 44,650,569 total_mem_bytes: 74,109,001 side_exit_count: 15,458,443 total_insn_count: 934,491,306 vm_insn_count: 166,025,364 zjit_insn_count: 768,465,942 ratio_in_zjit: 82.2% ``` </details> ### rails-bench <details> <summary>before patch</summary> ``` Average of last 10, non-warmup iters: 1254ms ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (52.8% of total 39,182,033): Hash#key?: 3,141,634 ( 8.0%) Regexp#match?: 2,420,227 ( 6.2%) Hash#fetch: 2,245,557 ( 5.7%) Array#any?: 1,157,418 ( 3.0%) Hash#delete: 1,114,346 ( 2.8%) Integer#===: 1,098,163 ( 2.8%) String.new: 1,004,713 ( 2.6%) MatchData#[]: 831,442 ( 2.1%) String#b: 797,913 ( 2.0%) String#to_sym: 680,943 ( 1.7%) Kernel#dup: 680,022 ( 1.7%) Array#all?: 650,132 ( 1.7%) Fiber.current: 649,003 ( 1.7%) Array#join: 641,038 ( 1.6%) Array#include?: 613,837 ( 1.6%) Kernel#Array: 610,311 ( 1.6%) String#<<: 606,240 ( 1.5%) Symbol#end_with?: 598,807 ( 1.5%) String#force_encoding: 593,535 ( 1.5%) Kernel#respond_to?: 550,441 ( 1.4%) Top-20 calls to C functions from JIT code (75.2% of total 260,204,372): rb_vm_opt_send_without_block: 52,620,850 (20.2%) rb_hash_aref: 22,920,184 ( 8.8%) rb_vm_env_write: 19,484,445 ( 7.5%) rb_vm_send: 16,570,926 ( 6.4%) rb_zjit_writebarrier_check_immediate: 13,628,686 ( 5.2%) rb_vm_getinstancevariable: 12,378,112 ( 4.8%) rb_ivar_get_at_no_ractor_check: 12,208,856 ( 4.7%) rb_vm_invokesuper: 8,086,664 ( 3.1%) rb_hash_aset: 5,043,532 ( 1.9%) rb_obj_is_kind_of: 4,431,294 ( 1.7%) rb_vm_invokeblock: 4,036,483 ( 1.6%) Hash#key?: 3,141,634 ( 1.2%) rb_vm_opt_getconstant_path: 3,051,909 ( 1.2%) rb_class_allocate_instance: 2,878,743 ( 1.1%) rb_hash_new_with_size: 2,873,398 ( 1.1%) rb_ec_ary_new_from_values: 2,584,790 ( 1.0%) rb_str_concat_literals: 2,450,752 ( 0.9%) Regexp#match?: 2,420,227 ( 0.9%) rb_obj_alloc: 2,419,180 ( 0.9%) rb_vm_setinstancevariable: 2,357,067 ( 0.9%) Top-2 not optimized method types for send (100.0% of total 8,550,761): iseq: 8,518,290 (99.6%) optimized: 32,471 ( 0.4%) Top-2 not optimized method types for send_without_block (100.0% of total 790,792): optimized_send: 608,036 (76.9%) null: 182,756 (23.1%) Top-2 not optimized method types for super (100.0% of total 6,689,860): cfunc: 6,640,181 (99.3%) attrset: 49,679 ( 0.7%) Top-3 instructions with uncategorized fallback reason (100.0% of total 5,911,882): invokeblock: 4,036,483 (68.3%) sendforward: 1,871,601 (31.7%) opt_send_without_block: 3,798 ( 0.1%) Top-20 send fallback reasons (100.0% of total 83,186,524): send_without_block_polymorphic: 33,814,235 (40.6%) send_not_optimized_method_type: 8,550,761 (10.3%) send_without_block_no_profiles: 8,405,471 (10.1%) super_not_optimized_method_type: 6,689,860 ( 8.0%) uncategorized: 5,911,882 ( 7.1%) one_or_more_complex_arg_pass: 5,502,146 ( 6.6%) send_no_profiles: 4,700,820 ( 5.7%) send_polymorphic: 3,318,564 ( 4.0%) send_without_block_not_optimized_need_permission: 1,274,177 ( 1.5%) singleton_class_seen: 1,101,973 ( 1.3%) too_many_args_for_lir: 905,412 ( 1.1%) super_complex_args_pass: 829,842 ( 1.0%) send_without_block_not_optimized_method_type_optimized: 608,036 ( 0.7%) send_without_block_megamorphic: 565,874 ( 0.7%) super_target_complex_args_pass: 414,600 ( 0.5%) send_without_block_not_optimized_method_type: 182,756 ( 0.2%) obj_to_string_not_string: 158,141 ( 0.2%) super_call_with_block: 100,004 ( 0.1%) send_without_block_direct_keyword_mismatch: 99,588 ( 0.1%) super_polymorphic: 52,358 ( 0.1%) Top-2 setivar fallback reasons (100.0% of total 2,357,067): not_monomorphic: 2,255,283 (95.7%) not_t_object: 101,784 ( 4.3%) Top-1 getivar fallback reasons (100.0% of total 12,378,137): not_monomorphic: 12,378,137 (100.0%) Top-2 definedivar fallback reasons (100.0% of total 350,548): not_monomorphic: 350,461 (100.0%) not_t_object: 87 ( 0.0%) Top-6 invokeblock handler (100.0% of total 4,036,483): monomorphic_iseq: 2,189,057 (54.2%) polymorphic: 1,207,002 (29.9%) monomorphic_other: 334,248 ( 8.3%) monomorphic_ifunc: 221,225 ( 5.5%) megamorphic: 84,439 ( 2.1%) no_profiles: 512 ( 0.0%) Top-9 popular complex argument-parameter features not optimized (100.0% of total 7,096,505): param_kw_opt: 1,834,705 (25.9%) param_forwardable: 1,824,953 (25.7%) param_block: 1,792,214 (25.3%) param_rest: 861,894 (12.1%) caller_kw_splat: 297,937 ( 4.2%) caller_splat: 283,669 ( 4.0%) param_kwrest: 200,208 ( 2.8%) caller_blockarg: 752 ( 0.0%) caller_kwarg: 173 ( 0.0%) Top-1 compile error reasons (100.0% of total 391,562): exception_handler: 391,562 (100.0%) Top-7 unhandled YARV insns (100.0% of total 1,899,393): getblockparam: 898,862 (47.3%) invokesuperforward: 498,993 (26.3%) getconstant: 400,945 (21.1%) expandarray: 49,985 ( 2.6%) setblockparam: 49,972 ( 2.6%) checkmatch: 480 ( 0.0%) once: 156 ( 0.0%) Top-2 unhandled HIR insns (100.0% of total 268,151): throw: 232,560 (86.7%) invokebuiltin: 35,591 (13.3%) Top-19 side exit reasons (100.0% of total 9,609,677): guard_shape_failure: 2,498,160 (26.0%) block_param_proxy_not_iseq_or_ifunc: 1,988,408 (20.7%) unhandled_yarv_insn: 1,899,393 (19.8%) guard_type_failure: 1,722,167 (17.9%) compile_error: 391,562 ( 4.1%) unhandled_newarray_send_pack: 298,017 ( 3.1%) unhandled_hir_insn: 268,151 ( 2.8%) patchpoint_method_redefined: 200,632 ( 2.1%) unhandled_block_arg: 151,295 ( 1.6%) block_param_proxy_modified: 124,245 ( 1.3%) guard_less_failure: 50,126 ( 0.5%) fixnum_lshift_overflow: 9,985 ( 0.1%) patchpoint_stable_constant_names: 6,366 ( 0.1%) fixnum_mult_overflow: 570 ( 0.0%) obj_to_string_fallback: 429 ( 0.0%) patchpoint_no_ep_escape: 109 ( 0.0%) interrupt: 48 ( 0.0%) guard_super_method_entry: 8 ( 0.0%) guard_greater_eq_failure: 6 ( 0.0%) send_count: 328,547,991 dynamic_send_count: 83,186,524 (25.3%) optimized_send_count: 245,361,467 (74.7%) dynamic_setivar_count: 2,357,067 ( 0.7%) dynamic_getivar_count: 12,378,137 ( 3.8%) dynamic_definedivar_count: 350,548 ( 0.1%) iseq_optimized_send_count: 93,424,465 (28.4%) inline_cfunc_optimized_send_count: 98,338,280 (29.9%) inline_iseq_optimized_send_count: 9,338,763 ( 2.8%) non_variadic_cfunc_optimized_send_count: 26,452,910 ( 8.1%) variadic_cfunc_optimized_send_count: 17,807,049 ( 5.4%) compiled_iseq_count: 2,887 failed_iseq_count: 0 compile_time: 877ms profile_time: 32ms gc_time: 11ms invalidation_time: 15ms vm_write_pc_count: 284,341,923 vm_write_sp_count: 284,341,923 vm_write_locals_count: 272,137,494 vm_write_stack_count: 272,137,494 vm_write_to_parent_iseq_local_count: 1,079,867 vm_read_from_parent_iseq_local_count: 30,816,135 guard_type_count: 313,667,907 guard_type_exit_ratio: 0.5% guard_shape_count: 0 code_region_bytes: 14,417,920 zjit_alloc_bytes: 19,075,183 total_mem_bytes: 33,493,103 side_exit_count: 9,609,677 total_insn_count: 1,706,360,231 vm_insn_count: 124,793,155 zjit_insn_count: 1,581,567,076 ratio_in_zjit: 92.7% ``` </details> <details> <summary>after patch</summary> ``` Average of last 10, non-warmup iters: 1136ms ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (52.8% of total 39,182,033): Hash#key?: 3,141,634 ( 8.0%) Regexp#match?: 2,420,227 ( 6.2%) Hash#fetch: 2,245,557 ( 5.7%) Array#any?: 1,157,418 ( 3.0%) Hash#delete: 1,114,346 ( 2.8%) Integer#===: 1,098,163 ( 2.8%) String.new: 1,004,713 ( 2.6%) MatchData#[]: 831,442 ( 2.1%) String#b: 797,913 ( 2.0%) String#to_sym: 680,943 ( 1.7%) Kernel#dup: 680,022 ( 1.7%) Array#all?: 650,132 ( 1.7%) Fiber.current: 649,003 ( 1.7%) Array#join: 641,038 ( 1.6%) Array#include?: 613,837 ( 1.6%) Kernel#Array: 610,311 ( 1.6%) String#<<: 606,240 ( 1.5%) Symbol#end_with?: 598,807 ( 1.5%) String#force_encoding: 593,535 ( 1.5%) Kernel#respond_to?: 550,441 ( 1.4%) Top-20 calls to C functions from JIT code (74.8% of total 261,805,313): rb_vm_opt_send_without_block: 52,621,173 (20.1%) rb_hash_aref: 22,920,184 ( 8.8%) rb_vm_env_write: 19,484,925 ( 7.4%) rb_vm_send: 16,571,020 ( 6.3%) rb_zjit_writebarrier_check_immediate: 13,780,332 ( 5.3%) rb_vm_getinstancevariable: 12,378,114 ( 4.7%) rb_ivar_get_at_no_ractor_check: 12,208,856 ( 4.7%) rb_vm_invokesuper: 8,086,666 ( 3.1%) rb_hash_aset: 5,043,537 ( 1.9%) rb_obj_is_kind_of: 4,431,299 ( 1.7%) rb_vm_invokeblock: 4,036,481 ( 1.5%) Hash#key?: 3,141,634 ( 1.2%) rb_vm_opt_getconstant_path: 3,051,909 ( 1.2%) rb_class_allocate_instance: 2,878,746 ( 1.1%) rb_hash_new_with_size: 2,873,398 ( 1.1%) rb_ec_ary_new_from_values: 2,585,224 ( 1.0%) rb_str_concat_literals: 2,450,752 ( 0.9%) Regexp#match?: 2,420,227 ( 0.9%) rb_obj_alloc: 2,419,182 ( 0.9%) rb_vm_setinstancevariable: 2,357,067 ( 0.9%) Top-2 not optimized method types for send (100.0% of total 8,550,761): iseq: 8,518,290 (99.6%) optimized: 32,471 ( 0.4%) Top-2 not optimized method types for send_without_block (100.0% of total 790,792): optimized_send: 608,036 (76.9%) null: 182,756 (23.1%) Top-2 not optimized method types for super (100.0% of total 6,689,860): cfunc: 6,640,181 (99.3%) attrset: 49,679 ( 0.7%) Top-3 instructions with uncategorized fallback reason (100.0% of total 5,911,883): invokeblock: 4,036,481 (68.3%) sendforward: 1,871,601 (31.7%) opt_send_without_block: 3,801 ( 0.1%) Top-20 send fallback reasons (100.0% of total 83,186,941): send_without_block_polymorphic: 33,814,528 (40.6%) send_not_optimized_method_type: 8,550,761 (10.3%) send_without_block_no_profiles: 8,405,497 (10.1%) super_not_optimized_method_type: 6,689,860 ( 8.0%) uncategorized: 5,911,883 ( 7.1%) one_or_more_complex_arg_pass: 5,502,147 ( 6.6%) send_no_profiles: 4,700,820 ( 5.7%) send_polymorphic: 3,318,658 ( 4.0%) send_without_block_not_optimized_need_permission: 1,274,177 ( 1.5%) singleton_class_seen: 1,101,973 ( 1.3%) too_many_args_for_lir: 905,412 ( 1.1%) super_complex_args_pass: 829,842 ( 1.0%) send_without_block_not_optimized_method_type_optimized: 608,036 ( 0.7%) send_without_block_megamorphic: 565,874 ( 0.7%) super_target_complex_args_pass: 414,600 ( 0.5%) send_without_block_not_optimized_method_type: 182,756 ( 0.2%) obj_to_string_not_string: 158,141 ( 0.2%) super_call_with_block: 100,004 ( 0.1%) send_without_block_direct_keyword_mismatch: 99,588 ( 0.1%) super_polymorphic: 52,360 ( 0.1%) Top-2 setivar fallback reasons (100.0% of total 2,357,067): not_monomorphic: 2,255,283 (95.7%) not_t_object: 101,784 ( 4.3%) Top-1 getivar fallback reasons (100.0% of total 12,378,139): not_monomorphic: 12,378,139 (100.0%) Top-2 definedivar fallback reasons (100.0% of total 350,548): not_monomorphic: 350,461 (100.0%) not_t_object: 87 ( 0.0%) Top-6 invokeblock handler (100.0% of total 4,036,481): monomorphic_iseq: 2,189,057 (54.2%) polymorphic: 1,207,002 (29.9%) monomorphic_other: 334,248 ( 8.3%) monomorphic_ifunc: 221,223 ( 5.5%) megamorphic: 84,439 ( 2.1%) no_profiles: 512 ( 0.0%) Top-9 popular complex argument-parameter features not optimized (100.0% of total 7,096,506): param_kw_opt: 1,834,706 (25.9%) param_forwardable: 1,824,953 (25.7%) param_block: 1,792,214 (25.3%) param_rest: 861,894 (12.1%) caller_kw_splat: 297,937 ( 4.2%) caller_splat: 283,669 ( 4.0%) param_kwrest: 200,208 ( 2.8%) caller_blockarg: 752 ( 0.0%) caller_kwarg: 173 ( 0.0%) Top-1 compile error reasons (100.0% of total 391,562): exception_handler: 391,562 (100.0%) Top-6 unhandled YARV insns (100.0% of total 1,000,531): invokesuperforward: 498,993 (49.9%) getconstant: 400,945 (40.1%) expandarray: 49,985 ( 5.0%) setblockparam: 49,972 ( 5.0%) checkmatch: 480 ( 0.0%) once: 156 ( 0.0%) Top-2 unhandled HIR insns (100.0% of total 268,154): throw: 232,560 (86.7%) invokebuiltin: 35,594 (13.3%) Top-19 side exit reasons (100.0% of total 8,710,811): guard_shape_failure: 2,498,161 (28.7%) block_param_proxy_not_iseq_or_ifunc: 1,988,408 (22.8%) guard_type_failure: 1,722,168 (19.8%) unhandled_yarv_insn: 1,000,531 (11.5%) compile_error: 391,562 ( 4.5%) unhandled_newarray_send_pack: 298,017 ( 3.4%) unhandled_hir_insn: 268,154 ( 3.1%) patchpoint_method_redefined: 200,632 ( 2.3%) unhandled_block_arg: 151,295 ( 1.7%) block_param_proxy_modified: 124,245 ( 1.4%) guard_less_failure: 50,126 ( 0.6%) fixnum_lshift_overflow: 9,985 ( 0.1%) patchpoint_stable_constant_names: 6,366 ( 0.1%) fixnum_mult_overflow: 570 ( 0.0%) obj_to_string_fallback: 429 ( 0.0%) patchpoint_no_ep_escape: 109 ( 0.0%) interrupt: 39 ( 0.0%) guard_super_method_entry: 8 ( 0.0%) guard_greater_eq_failure: 6 ( 0.0%) send_count: 328,747,903 dynamic_send_count: 83,186,941 (25.3%) optimized_send_count: 245,560,962 (74.7%) dynamic_setivar_count: 2,357,067 ( 0.7%) dynamic_getivar_count: 12,378,139 ( 3.8%) dynamic_definedivar_count: 350,548 ( 0.1%) iseq_optimized_send_count: 93,623,831 (28.5%) inline_cfunc_optimized_send_count: 98,338,311 (29.9%) inline_iseq_optimized_send_count: 9,338,766 ( 2.8%) non_variadic_cfunc_optimized_send_count: 26,453,005 ( 8.0%) variadic_cfunc_optimized_send_count: 17,807,049 ( 5.4%) compiled_iseq_count: 2,888 failed_iseq_count: 0 compile_time: 858ms profile_time: 29ms gc_time: 59ms invalidation_time: 15ms vm_write_pc_count: 285,990,091 vm_write_sp_count: 285,990,091 vm_write_locals_count: 272,886,376 vm_write_stack_count: 272,886,376 vm_write_to_parent_iseq_local_count: 1,079,877 vm_read_from_parent_iseq_local_count: 30,816,135 guard_type_count: 314,169,071 guard_type_exit_ratio: 0.5% guard_shape_count: 0 code_region_bytes: 14,401,536 zjit_alloc_bytes: 19,128,598 total_mem_bytes: 33,530,134 side_exit_count: 8,710,811 total_insn_count: 1,705,461,649 vm_insn_count: 121,244,824 zjit_insn_count: 1,584,216,825 ratio_in_zjit: 92.9% ``` </details>
7 daysZJIT: Support optional keyword arguments in direct send (#15873)Randy Stauner
This fills in constants when unspecified optional keyword args have static default values. For complex defaults we calculate the kw_bits and utilize the checkkeyword logic we already had. The following benchmarks used to register param_kw_opt. Some of them (like graphql*) just trade that for some other complexity, or "too_many_args_for_lir". Notable improvements include activerecord where the previous param_kw_opt count has a corresponding drop in complex args and dynamic_send_count and a nearly equal rise in optimized_send_count. The gains are similar but not as complete in hexapdf, liquid-render, lobsters, railsbench, shipit. | Benchmark | param_kw_opt | Δ one_or_more_complex | Δ too_many_args | Δ dynamic_send | Δ optimized_send | |-----------|-------------:|----------------------:|----------------:|---------------:|-----------------:| | activerecord | 6,307,141 | -6,253,823 | +4,084 | -6,306,223 | +6,279,766 | | blurhash | 21 | -21 | +0 | -23 | +20 | | chunky-png | 813,604 | -813,604 | +0 | -813,616 | +813,556 | | erubi-rails | 1,590,395 | -590,274 | +35,578 | -552,914 | +550,826 | | fluentd | 4,906 | -4,854 | +21 | -5,745 | +5,080 | | graphql | 1,610,439 | -1,610,432 | +1,605,751 | -4,688 | +4,628 | | graphql-native | 16,332,386 | -16,332,375 | +16,309,681 | -22,701 | +22,638 | | hexapdf | 9,165,465 | -9,124,509 | +203,754 | -8,920,727 | +8,839,295 | | liquid-compile | 14,817 | -14,792 | +0 | -14,705 | +15,045 | | liquid-render | 3,994,905 | -3,994,901 | +0 | -3,994,868 | +3,020,779 | | lobsters | 2,467,510 | -2,297,298 | +205,610 | -2,216,583 | +1,694,092 | | protoboeuf | 11,521 | -11,521 | +0 | -11,523 | +11,520 | | psych-load | 77,612 | -77,609 | +29,942 | -77,613 | -12,242 | | rack | 2,743 | -2,742 | +0 | -2,750 | +2,668 | | railsbench | 3,579,778 | -2,517,615 | +432,575 | -2,084,480 | +1,882,928 | | ruby-lsp | 287,171 | -379,716 | +37 | -409,368 | -267,248 | | rubyboy | 5,993,004 | -5,993,003 | +0 | -5,993,006 | +5,992,993 | | sequel | 182,652 | -182,631 | +0 | -182,563 | +122,687 | | shipit | 3,289,456 | -2,778,419 | +306,867 | -3,201,395 | +1,068,505 | | tinygql | 2,732 | -2,732 | +1 | -2,734 | +2,729 |
7 daysZJIT: Add a smoke test for --zjit-trace-exitsAlan Wu
Better than nothing!
7 daysZJIT: Compile IsA into load + compare for String/Array/Hash (#15878)Jeff Zhang
Resolves https://github.com/Shopify/ruby/issues/880 Implemented this by using the code generation for `GuardType` as a reference. Not sure if this is the best way to go about it, but it seems to work.
9 days[DOC] Remove _emphasis_ in code blocks which is not handled as emphasis ↵tomoya ishida
anymore (#15901)
11 daysZJIT: Specialize OPTIMIZED_METHOD_TYPE_CALL (#15859)Nozomi Hijikata
Closes: https://github.com/Shopify/ruby/issues/865 ## Benchmark ### lobsters - wall clock time - before patch: Average of last 10, non-warmup iters: 809ms - after patch: Average of last 10, non-warmup iters: 754ms - zjit stats below <details> <summary>before patch</summary> ``` ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (54.9% of total 18,003,698): Hash#fetch: 3,184,106 (17.7%) Regexp#match?: 707,148 ( 3.9%) Hash#key?: 689,879 ( 3.8%) String#sub!: 489,841 ( 2.7%) Array#include?: 470,648 ( 2.6%) Set#include?: 397,520 ( 2.2%) String#<<: 396,279 ( 2.2%) String#start_with?: 373,666 ( 2.1%) Kernel#dup: 352,617 ( 2.0%) Array#any?: 350,454 ( 1.9%) Hash#delete: 331,784 ( 1.8%) String.new: 307,248 ( 1.7%) Integer#===: 262,336 ( 1.5%) Symbol#end_with?: 255,538 ( 1.4%) Kernel#is_a?: 247,292 ( 1.4%) Process.clock_gettime: 221,588 ( 1.2%) Integer#>: 219,718 ( 1.2%) String#match?: 216,903 ( 1.2%) String#downcase: 213,108 ( 1.2%) Integer#<=: 202,617 ( 1.1%) Top-20 calls to C functions from JIT code (80.3% of total 130,255,689): rb_vm_opt_send_without_block: 28,329,698 (21.7%) rb_hash_aref: 8,992,191 ( 6.9%) rb_vm_env_write: 8,526,087 ( 6.5%) rb_vm_send: 8,337,448 ( 6.4%) rb_zjit_writebarrier_check_immediate: 7,809,310 ( 6.0%) rb_obj_is_kind_of: 6,098,929 ( 4.7%) rb_vm_getinstancevariable: 5,783,055 ( 4.4%) rb_vm_invokesuper: 5,038,443 ( 3.9%) rb_ivar_get_at_no_ractor_check: 4,762,093 ( 3.7%) rb_ary_entry: 4,283,966 ( 3.3%) rb_hash_aset: 2,429,862 ( 1.9%) rb_vm_setinstancevariable: 2,343,571 ( 1.8%) rb_vm_opt_getconstant_path: 2,284,810 ( 1.8%) Hash#fetch: 1,778,515 ( 1.4%) fetch: 1,405,591 ( 1.1%) rb_vm_invokeblock: 1,381,332 ( 1.1%) rb_str_buf_append: 1,362,272 ( 1.0%) rb_ec_ary_new_from_values: 1,324,997 ( 1.0%) rb_class_allocate_instance: 1,288,936 ( 1.0%) rb_hash_new_with_size: 998,628 ( 0.8%) Top-2 not optimized method types for send (100.0% of total 4,896,274): iseq: 4,893,452 (99.9%) null: 2,822 ( 0.1%) Top-4 not optimized method types for send_without_block (100.0% of total 782,296): optimized_send: 479,562 (61.3%) optimized_call: 256,609 (32.8%) null: 41,967 ( 5.4%) optimized_block_call: 4,158 ( 0.5%) Top-4 instructions with uncategorized fallback reason (100.0% of total 7,250,555): invokesuper: 5,038,443 (69.5%) invokeblock: 1,381,332 (19.1%) sendforward: 798,924 (11.0%) opt_send_without_block: 31,856 ( 0.4%) Top-18 send fallback reasons (100.0% of total 43,885,845): send_without_block_polymorphic: 18,533,639 (42.2%) uncategorized: 7,250,555 (16.5%) send_not_optimized_method_type: 4,896,274 (11.2%) send_without_block_no_profiles: 4,741,871 (10.8%) send_no_profiles: 2,865,577 ( 6.5%) one_or_more_complex_arg_pass: 2,825,240 ( 6.4%) send_without_block_not_optimized_method_type_optimized: 740,329 ( 1.7%) send_without_block_megamorphic: 709,818 ( 1.6%) send_polymorphic: 541,186 ( 1.2%) send_without_block_not_optimized_need_permission: 382,622 ( 0.9%) too_many_args_for_lir: 173,244 ( 0.4%) argc_param_mismatch: 50,382 ( 0.1%) send_without_block_not_optimized_method_type: 41,967 ( 0.1%) send_without_block_cfunc_array_variadic: 36,302 ( 0.1%) obj_to_string_not_string: 34,169 ( 0.1%) send_without_block_direct_keyword_mismatch: 32,436 ( 0.1%) send_megamorphic: 28,613 ( 0.1%) ccall_with_frame_too_many_args: 1,621 ( 0.0%) Top-4 setivar fallback reasons (100.0% of total 2,343,571): not_monomorphic: 2,120,856 (90.5%) not_t_object: 125,163 ( 5.3%) too_complex: 97,531 ( 4.2%) new_shape_needs_extension: 21 ( 0.0%) Top-2 getivar fallback reasons (100.0% of total 5,908,168): not_monomorphic: 5,658,909 (95.8%) too_complex: 249,259 ( 4.2%) Top-3 definedivar fallback reasons (100.0% of total 405,079): not_monomorphic: 397,150 (98.0%) too_complex: 5,122 ( 1.3%) not_t_object: 2,807 ( 0.7%) Top-6 invokeblock handler (100.0% of total 1,381,332): monomorphic_iseq: 685,359 (49.6%) polymorphic: 521,992 (37.8%) monomorphic_other: 104,640 ( 7.6%) monomorphic_ifunc: 55,505 ( 4.0%) no_profiles: 9,164 ( 0.7%) megamorphic: 4,672 ( 0.3%) Top-9 popular complex argument-parameter features not optimized (100.0% of total 3,097,538): param_kw_opt: 1,333,367 (43.0%) param_block: 632,885 (20.4%) param_forwardable: 600,601 (19.4%) param_rest: 329,020 (10.6%) param_kwrest: 119,971 ( 3.9%) caller_kw_splat: 39,001 ( 1.3%) caller_splat: 36,785 ( 1.2%) caller_blockarg: 5,798 ( 0.2%) caller_kwarg: 110 ( 0.0%) Top-1 compile error reasons (100.0% of total 186,900): exception_handler: 186,900 (100.0%) Top-7 unhandled YARV insns (100.0% of total 186,598): getblockparam: 99,414 (53.3%) invokesuperforward: 81,667 (43.8%) setblockparam: 2,837 ( 1.5%) getconstant: 1,537 ( 0.8%) checkmatch: 616 ( 0.3%) expandarray: 360 ( 0.2%) once: 167 ( 0.1%) Top-3 unhandled HIR insns (100.0% of total 236,962): throw: 198,474 (83.8%) invokebuiltin: 35,767 (15.1%) array_max: 2,721 ( 1.1%) Top-19 side exit reasons (100.0% of total 15,427,184): guard_type_failure: 6,865,696 (44.5%) guard_shape_failure: 6,779,586 (43.9%) block_param_proxy_not_iseq_or_ifunc: 1,030,319 ( 6.7%) unhandled_hir_insn: 236,962 ( 1.5%) compile_error: 186,900 ( 1.2%) unhandled_yarv_insn: 186,598 ( 1.2%) fixnum_mult_overflow: 50,739 ( 0.3%) block_param_proxy_modified: 28,119 ( 0.2%) patchpoint_no_singleton_class: 14,903 ( 0.1%) unhandled_newarray_send_pack: 14,481 ( 0.1%) fixnum_lshift_overflow: 10,085 ( 0.1%) patchpoint_stable_constant_names: 9,198 ( 0.1%) patchpoint_no_ep_escape: 7,815 ( 0.1%) expandarray_failure: 4,533 ( 0.0%) patchpoint_method_redefined: 662 ( 0.0%) obj_to_string_fallback: 277 ( 0.0%) guard_less_failure: 163 ( 0.0%) interrupt: 128 ( 0.0%) guard_greater_eq_failure: 20 ( 0.0%) send_count: 151,233,937 dynamic_send_count: 43,885,845 (29.0%) optimized_send_count: 107,348,092 (71.0%) dynamic_setivar_count: 2,343,571 ( 1.5%) dynamic_getivar_count: 5,908,168 ( 3.9%) dynamic_definedivar_count: 405,079 ( 0.3%) iseq_optimized_send_count: 37,324,023 (24.7%) inline_cfunc_optimized_send_count: 46,056,028 (30.5%) inline_iseq_optimized_send_count: 3,756,875 ( 2.5%) non_variadic_cfunc_optimized_send_count: 11,618,909 ( 7.7%) variadic_cfunc_optimized_send_count: 8,592,257 ( 5.7%) compiled_iseq_count: 5,289 failed_iseq_count: 0 compile_time: 1,664ms profile_time: 13ms gc_time: 20ms invalidation_time: 479ms vm_write_pc_count: 127,571,422 vm_write_sp_count: 127,571,422 vm_write_locals_count: 122,781,971 vm_write_stack_count: 122,781,971 vm_write_to_parent_iseq_local_count: 689,945 vm_read_from_parent_iseq_local_count: 14,721,820 guard_type_count: 167,633,896 guard_type_exit_ratio: 4.1% guard_shape_count: 0 code_region_bytes: 38,912,000 zjit_alloc_bytes: 40,542,102 total_mem_bytes: 79,454,102 side_exit_count: 15,427,184 total_insn_count: 927,373,567 vm_insn_count: 156,976,359 zjit_insn_count: 770,397,208 ratio_in_zjit: 83.1% ``` </details> <details> <summary>after patch</summary> ``` ***ZJIT: Printing ZJIT statistics on exit*** Top-20 not inlined C methods (55.0% of total 18,012,630): Hash#fetch: 3,184,101 (17.7%) Regexp#match?: 707,150 ( 3.9%) Hash#key?: 689,871 ( 3.8%) String#sub!: 489,841 ( 2.7%) Array#include?: 470,648 ( 2.6%) Set#include?: 397,520 ( 2.2%) String#<<: 396,279 ( 2.2%) String#start_with?: 382,538 ( 2.1%) Kernel#dup: 352,617 ( 2.0%) Array#any?: 350,454 ( 1.9%) Hash#delete: 331,802 ( 1.8%) String.new: 307,248 ( 1.7%) Integer#===: 262,336 ( 1.5%) Symbol#end_with?: 255,540 ( 1.4%) Kernel#is_a?: 247,292 ( 1.4%) Process.clock_gettime: 221,588 ( 1.2%) Integer#>: 219,718 ( 1.2%) String#match?: 216,905 ( 1.2%) String#downcase: 213,107 ( 1.2%) Integer#<=: 202,617 ( 1.1%) Top-20 calls to C functions from JIT code (80.1% of total 130,218,934): rb_vm_opt_send_without_block: 28,073,153 (21.6%) rb_hash_aref: 8,992,167 ( 6.9%) rb_vm_env_write: 8,526,089 ( 6.5%) rb_vm_send: 8,337,453 ( 6.4%) rb_zjit_writebarrier_check_immediate: 7,786,426 ( 6.0%) rb_obj_is_kind_of: 6,098,927 ( 4.7%) rb_vm_getinstancevariable: 5,783,053 ( 4.4%) rb_vm_invokesuper: 5,038,444 ( 3.9%) rb_ivar_get_at_no_ractor_check: 4,762,093 ( 3.7%) rb_ary_entry: 4,283,965 ( 3.3%) rb_hash_aset: 2,429,864 ( 1.9%) rb_vm_setinstancevariable: 2,343,573 ( 1.8%) rb_vm_opt_getconstant_path: 2,284,809 ( 1.8%) Hash#fetch: 1,778,510 ( 1.4%) fetch: 1,405,591 ( 1.1%) rb_vm_invokeblock: 1,381,329 ( 1.1%) rb_str_buf_append: 1,362,272 ( 1.0%) rb_ec_ary_new_from_values: 1,325,005 ( 1.0%) rb_class_allocate_instance: 1,288,944 ( 1.0%) rb_hash_new_with_size: 998,629 ( 0.8%) Top-2 not optimized method types for send (100.0% of total 4,896,276): iseq: 4,893,454 (99.9%) null: 2,822 ( 0.1%) Top-3 not optimized method types for send_without_block (100.0% of total 525,687): optimized_send: 479,562 (91.2%) null: 41,967 ( 8.0%) optimized_block_call: 4,158 ( 0.8%) Top-4 instructions with uncategorized fallback reason (100.0% of total 7,250,556): invokesuper: 5,038,444 (69.5%) invokeblock: 1,381,329 (19.1%) sendforward: 798,924 (11.0%) opt_send_without_block: 31,859 ( 0.4%) Top-18 send fallback reasons (100.0% of total 43,629,303): send_without_block_polymorphic: 18,533,669 (42.5%) uncategorized: 7,250,556 (16.6%) send_not_optimized_method_type: 4,896,276 (11.2%) send_without_block_no_profiles: 4,741,899 (10.9%) send_no_profiles: 2,865,579 ( 6.6%) one_or_more_complex_arg_pass: 2,825,242 ( 6.5%) send_without_block_megamorphic: 709,818 ( 1.6%) send_polymorphic: 541,187 ( 1.2%) send_without_block_not_optimized_method_type_optimized: 483,720 ( 1.1%) send_without_block_not_optimized_need_permission: 382,623 ( 0.9%) too_many_args_for_lir: 173,244 ( 0.4%) argc_param_mismatch: 50,382 ( 0.1%) send_without_block_not_optimized_method_type: 41,967 ( 0.1%) send_without_block_cfunc_array_variadic: 36,302 ( 0.1%) obj_to_string_not_string: 34,169 ( 0.1%) send_without_block_direct_keyword_mismatch: 32,436 ( 0.1%) send_megamorphic: 28,613 ( 0.1%) ccall_with_frame_too_many_args: 1,621 ( 0.0%) Top-4 setivar fallback reasons (100.0% of total 2,343,573): not_monomorphic: 2,120,858 (90.5%) not_t_object: 125,163 ( 5.3%) too_complex: 97,531 ( 4.2%) new_shape_needs_extension: 21 ( 0.0%) Top-2 getivar fallback reasons (100.0% of total 5,908,165): not_monomorphic: 5,658,912 (95.8%) too_complex: 249,253 ( 4.2%) Top-3 definedivar fallback reasons (100.0% of total 405,079): not_monomorphic: 397,150 (98.0%) too_complex: 5,122 ( 1.3%) not_t_object: 2,807 ( 0.7%) Top-6 invokeblock handler (100.0% of total 1,381,329): monomorphic_iseq: 685,363 (49.6%) polymorphic: 521,984 (37.8%) monomorphic_other: 104,640 ( 7.6%) monomorphic_ifunc: 55,505 ( 4.0%) no_profiles: 9,164 ( 0.7%) megamorphic: 4,673 ( 0.3%) Top-9 popular complex argument-parameter features not optimized (100.0% of total 3,094,719): param_kw_opt: 1,333,367 (43.1%) param_block: 632,886 (20.5%) param_forwardable: 600,605 (19.4%) param_rest: 329,019 (10.6%) param_kwrest: 119,971 ( 3.9%) caller_kw_splat: 39,001 ( 1.3%) caller_splat: 33,962 ( 1.1%) caller_blockarg: 5,798 ( 0.2%) caller_kwarg: 110 ( 0.0%) Top-1 compile error reasons (100.0% of total 186,917): exception_handler: 186,917 (100.0%) Top-7 unhandled YARV insns (100.0% of total 186,598): getblockparam: 99,414 (53.3%) invokesuperforward: 81,667 (43.8%) setblockparam: 2,837 ( 1.5%) getconstant: 1,537 ( 0.8%) checkmatch: 616 ( 0.3%) expandarray: 360 ( 0.2%) once: 167 ( 0.1%) Top-3 unhandled HIR insns (100.0% of total 236,969): throw: 198,475 (83.8%) invokebuiltin: 35,773 (15.1%) array_max: 2,721 ( 1.1%) Top-19 side exit reasons (100.0% of total 15,450,102): guard_type_failure: 6,888,596 (44.6%) guard_shape_failure: 6,779,586 (43.9%) block_param_proxy_not_iseq_or_ifunc: 1,030,319 ( 6.7%) unhandled_hir_insn: 236,969 ( 1.5%) compile_error: 186,917 ( 1.2%) unhandled_yarv_insn: 186,598 ( 1.2%) fixnum_mult_overflow: 50,739 ( 0.3%) block_param_proxy_modified: 28,119 ( 0.2%) patchpoint_no_singleton_class: 14,903 ( 0.1%) unhandled_newarray_send_pack: 14,481 ( 0.1%) fixnum_lshift_overflow: 10,085 ( 0.1%) patchpoint_stable_constant_names: 9,198 ( 0.1%) patchpoint_no_ep_escape: 7,815 ( 0.1%) expandarray_failure: 4,533 ( 0.0%) patchpoint_method_redefined: 662 ( 0.0%) obj_to_string_fallback: 277 ( 0.0%) guard_less_failure: 163 ( 0.0%) interrupt: 122 ( 0.0%) guard_greater_eq_failure: 20 ( 0.0%) send_count: 150,986,368 dynamic_send_count: 43,629,303 (28.9%) optimized_send_count: 107,357,065 (71.1%) dynamic_setivar_count: 2,343,573 ( 1.6%) dynamic_getivar_count: 5,908,165 ( 3.9%) dynamic_definedivar_count: 405,079 ( 0.3%) iseq_optimized_send_count: 37,324,039 (24.7%) inline_cfunc_optimized_send_count: 46,056,046 (30.5%) inline_iseq_optimized_send_count: 3,756,881 ( 2.5%) non_variadic_cfunc_optimized_send_count: 11,618,958 ( 7.7%) variadic_cfunc_optimized_send_count: 8,601,141 ( 5.7%) compiled_iseq_count: 5,289 failed_iseq_count: 0 compile_time: 1,700ms profile_time: 13ms gc_time: 21ms invalidation_time: 519ms vm_write_pc_count: 127,557,549 vm_write_sp_count: 127,557,549 vm_write_locals_count: 122,768,084 vm_write_stack_count: 122,768,084 vm_write_to_parent_iseq_local_count: 689,953 vm_read_from_parent_iseq_local_count: 14,730,705 guard_type_count: 167,853,730 guard_type_exit_ratio: 4.1% guard_shape_count: 0 code_region_bytes: 38,928,384 zjit_alloc_bytes: 41,103,415 total_mem_bytes: 80,031,799 side_exit_count: 15,450,102 total_insn_count: 927,432,364 vm_insn_count: 157,182,251 zjit_insn_count: 770,250,113 ratio_in_zjit: 83.1% ``` </details>
12 daysMatchData: Avoid large stack allocations in MatchData (GH-15872)Andrii Furmanets
12 daysZJIT: Inline ArrayArefnozomemein
12 daysMake `Array#map` and `Array#select` more tolerantNobuyoshi Nakada
Only when YJIT is enabled, the redefinition of `Array#<<` affects these methods.
13 daysZJIT: Optimize common `invokesuper` cases (#15816)Kevin Menard
* ZJIT: Profile `invokesuper` instructions * ZJIT: Introduce the `InvokeSuperDirect` HIR instruction The new instruction is an optimized version of `InvokeSuper` when we know the `super` target is an ISEQ. * ZJIT: Expand definition of unspecializable to more complex cases * ZJIT: Ensure `invokesuper` optimization works when the inheritance hierarchy is modified * ZJIT: Simplify `invokesuper` specialization to most common case Looking at ruby-bench, most `super` calls don't pass a block, which means we can use the already optimized `SendWithoutBlockDirect`. * ZJIT: Track `super` method entries directly to avoid GC issues Because the method entry isn't typed as a `VALUE`, we set up barriers on its `VALUE` fields. But, that was insufficient as the method entry itself could be collected in certain cases, resulting in dangling objects. Now we track the method entry as a `VALUE` and can more naturally mark it and its children. * ZJIT: Optimize `super` calls with simple argument forms * ZJIT: Report the reason why we can't optimize an `invokesuper` instance * ZJIT: Revise send fallback reasons for `super` calls * ZJIT: Assert `super` calls are `FCALL` and don't need visibily checks
13 daysYJIT: Add crashing test for --yjit-dump-insnsAlan Wu
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2026-01-12ZJIT: Snapshot FrameState with reordered args before direct sendRandy Stauner
You can see the reordered args in the new Snapshot right before the DirectSend insn: v14:Any = Snapshot FrameState { pc: 0x00, stack: [v6, v11, v13], locals: [] } PatchPoint MethodRedefined(Object@0x00, a@0x00, cme:0x00) PatchPoint NoSingletonClass(Object@0x00) v22:HeapObject[class_exact*:Object@VALUE(0x00)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x00)] - v23:BasicObject = SendWithoutBlockDirect v22, :a (0x00), v13, v11 - v16:Any = Snapshot FrameState { pc: 0x00, stack: [v23], locals: [] } + v23:Any = Snapshot FrameState { pc: 0x00, stack: [v6, v13, v11], locals: [] } + v24:BasicObject = SendWithoutBlockDirect v22, :a (0x00), v13, v11 + v16:Any = Snapshot FrameState { pc: 0x00, stack: [v24], locals: [] }
2026-01-11[ruby/prism] [Bug #21831] Fix denominator of rational float literalNobuyoshi Nakada
Denominators can contain underscores in fraction part as well as other numeric literals. [Bug #21831]: https://bugs.ruby-lang.org/issues/21831 https://github.com/ruby/prism/commit/e247cb58c7
2026-01-11Relax the flaky test threshold on RHEL 10.0 x86_64Nobuyoshi Nakada
2026-01-09YJIT: Add frozen guard for struct aset (#15835)Max Bernstein
We used to just skip this check (oops), but we should not allow modifying frozen objects.
2026-01-09Use `assert_ruby_status` if no assertionNobuyoshi Nakada
2026-01-09Make `assert_separately` tolerant to core method redefinitionsNobuyoshi Nakada
And split `TestRubyOptimization#test_objtostring` for each target class.
2026-01-08Fluent and/or is supported by Prism too nowNobuyoshi Nakada
2026-01-07ZJIT: Add ArrayAset instruction to HIR (#15747)Nozomi Hijikata
Inline `Array#[]=` into `ArrayAset`.
2026-01-05Fix sleep spurious wakeup from sigchld (#15802)Luke Gruber
When sleeping with `sleep`, currently the main thread can get woken up from sigchld from any thread (subprocess exited). The timer thread wakes up the main thread when this happens, as it checks for signals. The main thread then executes the ruby sigchld handler if one is registered and is supposed to go back to sleep immediately. This is not ideal but it's the way it's worked for a while. In commit 8d8159e7d8 I added writes to `th->status` before and after `wait_running_turn` in `thread_sched_to_waiting_until_wakeup`, which is called from `sleep`. This is usually the right way to set the thread's status, but `sleep` is an exception because the writes to `th->status` are done in `sleep_forever`. There's a loop that checks `th->status` in `sleep_forever`. When the main thread got woken up from sigchld it saw the changed `th->status` and continued to run the main thread instead of going back to sleep. The following script shows the error. It was returning instead of sleeping forever. ```ruby t = Thread.new do sleep 0.3 `echo hello` # Spawns subprocess puts "Subprocess exited" end puts "Main thread sleeping..." result = sleep # Should block forever puts "sleep returned: #{result.inspect}" ``` Fixes [Bug #21812]
2026-01-03Drop memberless Data/Struct#inspect trailing spaceShannon Skipper
Anonymous memberless Structs and Data were returning `#<struct >` and `#<data >` with a trailing space. Now they return `#<struct>` and `#<data>` to match attrless class behavior and look a bit more compact.
2026-01-03[Bug #21819] Data objects without members should also be frozenNobuyoshi Nakada
2026-01-03Convert Queue and SizedQueue to rb builtinJean Boussier
A large part of `thread_sync.c` was migrated already, might as well go all the way. It also allow to remove a bunch of Rdoc commands.
2026-01-02Add a test case for complex argument forward referenceJean Boussier
Using `eval` it's possible to reference a later argument, and this requires careful initialization of the stack.
2026-01-02Prefer dedicated assertionsNobuyoshi Nakada
2025-12-31Skip the hang-up test on WindowsNobuyoshi Nakada
2025-12-30Fix generational GC for weak referencesPeter Zhu
Fixes issue pointed out in https://bugs.ruby-lang.org/issues/21084#note-7. The following script crashes: wmap = ObjectSpace::WeakMap.new GC.disable # only manual GCs GC.start GC.start retain = [] 50.times do k = Object.new wmap[k] = true retain << k end GC.start # wmap promoted, other objects still young retain.clear GC.start(full_mark: false) wmap.keys.each(&:itself) # call method on keys to cause crash
2025-12-30[Bug #21784] Fix the Proc#source_location start_column for stabby lambdasBenoit Daloze
* Consistent with plain `blocks` and `for` blocks and methods where the source_location covers their entire definition. * Matches the documentation which mentions "where the definition starts/ends". * Partially reverts d357d50f0a74409446f4cccec78593373f5adf2f which was a workaround to be compatible with parse.y.
2025-12-30Reapply "[Feature #6012] Extend `source_location` for end positionBenoit Daloze
* This reverts commit 065c48cdf11a1c4cece84db44ed8624d294f8fd5. * This functionality is very valuable and has already taken 14 years to agree on the API. * Let's just document it's byte columns (in the next commit). * See https://bugs.ruby-lang.org/issues/21783#note-9
2025-12-30[Bug #21814] Fix negative bignum moduloNobuyoshi Nakada
If modulo is zero, do not apply bias even if the divisor is zero. `BIGNUM_POSITIVE_P` is true even on bignum zero.
2025-12-30Box: allocate classes as boxable when it happens in the root boxSatoshi Tagomori
Without this change, classes (including iclass) are allocated as un-boxable classes after initializing user boxes (after starting script evaluation). Under this situation, iclasses are created as un-boxabled class when core modules are included by a class in the root box, then it causes problems because it's in the root box but it can't have multiple classexts. This change makes it possible to allocate boxable classes even after initializing user boxes. Classes create in the root box will be boxable, and those can have 2 or more classexts.
2025-12-28Skip the hang-up test on mingwNobuyoshi Nakada
2025-12-26Remove `RUBY_GC_HEAP_INIT_SLOTS` environment variableNobuyoshi Nakada
2025-12-26Clarify the intent of the test for "ruby -h" to fit in 80x25Yusuke Endoh
2025-12-25Implement declaring weak referencesPeter Zhu
[Feature #21084] # Summary The current way of marking weak references uses `rb_gc_mark_weak(VALUE *ptr)`. This presents challenges because Ruby's GC is incremental, meaning that if the `ptr` changes (e.g. realloc'd or free'd), then we could have an invalid memory access. This also overwrites `*ptr = Qundef` if `*ptr` is dead, which prevents any cleanup to be run (e.g. freeing memory or deleting entries from hash tables). This ticket proposes `rb_gc_declare_weak_references` which declares that an object has weak references and calls a cleanup function after marking, allowing the object to clean up any memory for dead objects. # Introduction In [[Feature #19783]](https://bugs.ruby-lang.org/issues/19783), I introduced an API allowing objects to mark weak references, the function signature looks like this: ```c void rb_gc_mark_weak(VALUE *ptr); ``` `rb_gc_mark_weak` is called during the marking phase of the GC to specify that the memory at `ptr` holds a pointer to a Ruby object that is weakly referenced. `rb_gc_mark_weak` appends this pointer to a list that is processed after the marking phase of the GC. If the object at `*ptr` is no longer alive, then it overwrites the object reference with a special value (`*ptr = Qundef`). However, this API resulted in two challenges: 1. Ruby's default GC is incremental, which means that the GC is not ran in one phase, but rather split into chunks of work that interleaves with Ruby execution. The `ptr` passed into `rb_gc_mark_weak` could be on the malloc heap, and that memory could be realloc'd or even free'd. We had to use workarounds such as `rb_gc_remove_weak` to ensure that there were no illegal memory accesses. This made `rb_gc_mark_weak` difficult to use, impacted runtime performance, and increased memory usage. 2. When an object dies, `rb_gc_mark_weak` only overwites the reference with `Qundef`. This means that if we want to do any cleanup (e.g. free a piece of memory or delete a hash table entry), we could not do that and had to defer this process elsewhere (e.g. during marking or runtime). In this ticket, I'm proposing a new API for weak references. Instead of an object marking its weak references during the marking phase, the object declares that it has weak references using the `rb_gc_declare_weak_references` function. This declaration occurs during runtime (e.g. after the object has been created) rather than during GC. After an object declares that it has weak references, it will have its callback function called after marking as long as that object is alive. This callback function can then call a special function `rb_gc_handle_weak_references_alive_p` to determine whether its references are alive. This will allow the callback function to do whatever it wants on the object, allowing it to perform any cleanup work it needs. This significantly simplifies the code for `ObjectSpace::WeakMap` and `ObjectSpace::WeakKeyMap` because it no longer needs to have the workarounds for the limitations of `rb_gc_mark_weak`. # Performance The performance results below demonstrate that `ObjectSpace::WeakMap#[]=` is now about 60% faster because the implementation has been simplified and the number of allocations has been reduced. We can see that there is not a significant impact on the performance of `ObjectSpace::WeakMap#[]`. Base: ``` ObjectSpace::WeakMap#[]= 4.620M (± 6.4%) i/s (216.44 ns/i) - 23.342M in 5.072149s ObjectSpace::WeakMap#[] 30.967M (± 1.9%) i/s (32.29 ns/i) - 154.998M in 5.007157s ``` Branch: ``` ObjectSpace::WeakMap#[]= 7.336M (± 2.8%) i/s (136.31 ns/i) - 36.755M in 5.013983s ObjectSpace::WeakMap#[] 30.902M (± 5.4%) i/s (32.36 ns/i) - 155.901M in 5.064060s ``` Code: ``` require "bundler/inline" gemfile do source "https://rubygems.org" gem "benchmark-ips" end wmap = ObjectSpace::WeakMap.new key = Object.new val = Object.new wmap[key] = val Benchmark.ips do |x| x.report("ObjectSpace::WeakMap#[]=") do |times| i = 0 while i < times wmap[Object.new] = Object.new i += 1 end end x.report("ObjectSpace::WeakMap#[]") do |times| i = 0 while i < times wmap[key] wmap[val] # does not exist i += 1 end end end ``` # Alternative designs Currently, `rb_gc_declare_weak_references` is designed to be an internal-only API. This allows us to assume the object types that call `rb_gc_declare_weak_references`. In the future, if we want to open up this API to third parties, we may want to change this function to something like: ```c void rb_gc_add_cleaner(VALUE obj, void (*callback)(VALUE obj)); ``` This will allow the third party to implement a custom `callback` that gets called after the marking phase of GC to clean up any dead references. I chose not to implement this design because it is less efficient as we would need to store a mapping from `obj` to `callback`, which requires extra memory.
2025-12-25test_box: avoid failure with --program-suffix (#15734)Sorah Fukumori
2025-12-24Revert "Add link to Ruby options doc in help text"NARUSE, Yui
This reverts commit 31ff07ed1eb05d01f7da3c017d542137a3db1e94. * Don't add a test which only runs on production release * https://github.com/ruby/actions/actions/runs/20486784889/job/58870959976 * Don't add a new line to `ruby --help` * https://github.com/ruby/ruby/pull/14142#issuecomment-3689829564
2025-12-24Revert "Extract `ruby_api_version_name`"NARUSE, Yui
This reverts commit 9b576cd6255aba97e5e2f55f4b09f00c7dd0e839.
2025-12-24Prevent "warning: assigned but unused variable - it"Yusuke Endoh
2025-12-24Remove unintentional returnYusuke Endoh
2025-12-24Box: show the fully qualified URL of the Ruby::Box docSatoshi Tagomori
2025-12-24[Tests] Assert Module#set_temporary_name returns selfaguspe
The return value of Module#set_temporary_name was changed to return `self`, but the existing tests did not verify this.
2025-12-23Box: split the test for CI timeoutsSatoshi Tagomori
2025-12-20Test test_remove_instance_variable_re_embed separatelyPeter Zhu
Shape tree pollution could cause this test to flake.
2025-12-20Change test to define ivars in initialize methodPeter Zhu
Defining ivars in initialize method guarantees the object to be embedded.
2025-12-20Revert pack/unpack support for LEB128Nobuyoshi Nakada
https://bugs.ruby-lang.org/issues/21785#note-10 > It is too late to introduce it in Ruby 4.0, let's aim for 4.1. This reverts commits: * d0b72429a93e54f1f956b4aedfc25c57dc7001aa Add support for signed and unsigned LEB128 to pack/unpack. * 68a900e30b4ca1537d7975c3a619fd94fca7b084 add news for pack / unpack directives
2025-12-20fix for a test case that depends on rbuf sizeYO4