Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Differentiate T_ARRAY and array subclasses #7091

Merged
merged 2 commits into from Jan 10, 2023

Conversation

tenderlove
Copy link
Member

This commit teaches the YJIT context the difference between Arrays (objects with type T_ARRAY and class rb_cArray) vs Array subclasses (objects with type T_ARRAY but not class rb_cArray). It uses this information to reduce the number of guards emitted when using jit_guard_known_klass with rb_cArray, notably opt_aref

This commit teaches the YJIT context the difference between Arrays
(objects with type T_ARRAY and class rb_cArray) vs Array subclasses
(objects with type T_ARRAY but _not_ class rb_cArray).  It uses this
information to reduce the number of guards emitted when using
`jit_guard_known_klass` with rb_cArray, notably opt_aref
@matzbot matzbot requested a review from a team January 9, 2023 23:47
@noahgibbs
Copy link
Contributor

This sounds like a useful thing. Certainly I had good luck with the same change to strings. Have you run any benchmarks on it?

yjit/src/core.rs Outdated Show resolved Hide resolved
@maximecb
Copy link
Contributor

Looks like a good idea 👍

Could you include the output of --yjit-stats on railsbench before/after, and quickly benchmark railsbench and liquid-render? I don't expect to see a perf difference but the stats should change a bit, particularly inline code size.

@k0kubun
Copy link
Member

k0kubun commented Jan 10, 2023

Could you include the output of --yjit-stats on railsbench before/after

reminder: You can see this one on CI

Before

https://github.com/ruby/ruby/actions/runs/3878313771/jobs/6614355505

***YJIT: Printing YJIT statistics on exit***
method call exit reasons: 
               iseq_has_rest     109180 (31.4%)
                 iseq_zsuper      45902 (13.2%)
         args_splat_non_iseq      36456 (10.5%)
                   block_arg      32966 ( 9.5%)
            iseq_arity_error      28523 ( 8.2%)
         iseq_ruby2_keywords      24631 ( 7.1%)
              iseq_has_no_kw      17853 ( 5.1%)
         iseq_splat_with_opt      15316 ( 4.4%)
            args_splat_cfunc      13841 ( 4.0%)
                    kw_splat      12611 ( 3.6%)
    iseq_missing_optional_kw       3942 ( 1.1%)
             iseq_has_kwrest       3229 ( 0.9%)
               iseq_has_post       1958 ( 0.6%)
              refined_method        812 ( 0.2%)
       cfunc_ruby_array_varg        469 ( 0.1%)
                    keywords         90 ( 0.0%)
               zsuper_method          5 ( 0.0%)
invokeblock exit reasons: 
               ifunc       5691 (59.0%)
     iseq_arg0_splat       2000 (20.7%)
                proc       1848 (19.2%)
    iseq_tag_changed         76 ( 0.8%)
              symbol         32 ( 0.3%)
invokesuper exit reasons: 
         block       4716 (99.6%)
    me_changed         21 ( 0.4%)
leave exit reasons: 
        interp_return    1699583 (98.1%)
    start_pc_non_zero      33361 ( 1.9%)
         se_interrupt         38 ( 0.0%)
getblockparamproxy exit reasons: 
    block_param_modified         77 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons: 
    (all relevant counters are zero)
expandarray exit reasons: 
            splat       9944 (100.0%)
    rhs_too_small          4 ( 0.0%)
opt_getinlinecache exit reasons: 
    miss          2 (100.0%)
invalidation reasons: 
          method_lookup        316 (60.4%)
    constant_state_bump        149 (28.5%)
       constant_ic_fill         58 (11.1%)
bindings_allocations:         154
bindings_set:                   0
compiled_iseq_count:         2168
compiled_block_count:       19986
compiled_branch_count:      34954
block_next_count:            7062
defer_count:                 6618
freed_iseq_count:              35
invalidation_count:           523
constant_state_bumps:           0
inline_code_size:         2014869
outlined_code_size:       2014448
freed_code_size:                0
code_region_size:         4042752
yjit_alloc_size:         40497128
live_page_count:              247
freed_page_count:               0
code_gc_count:                  0
num_gc_obj_refs:            15879
object_shape_count:          2407
side_exit_count:           434463
total_exit_count:         2134046
total_insns_count:       88045532
vm_insns_count:           7341981
yjit_insns_count:        81138014
ratio_in_yjit:              91.7%
avg_len_in_yjit:             37.8
Top-20 most frequent exit ops (100.0% of exits):
    opt_send_without_block:     130268 (30.0%)
               invokesuper:     115611 (26.6%)
                      send:     101733 (23.4%)
               invokeblock:      36200 (8.3%)
                  opt_aref:      14024 (3.2%)
                     throw:      10460 (2.4%)
               expandarray:       9948 (2.3%)
             setlocal_WC_0:       8148 (1.9%)
                    opt_eq:       4966 (1.1%)
        getblockparamproxy:       2176 (0.5%)
                 opt_nil_p:        325 (0.1%)
               objtostring:        286 (0.1%)
                checkmatch:        154 (0.0%)
      opt_getconstant_path:         63 (0.0%)
                      once:         58 (0.0%)
                     leave:         38 (0.0%)
               opt_empty_p:          2 (0.0%)
                  opt_ltlt:          1 (0.0%)
                  opt_aset:          1 (0.0%)
          opt_regexpmatch2:          1 (0.0%)
itr #1: 6657ms

After

https://github.com/ruby/ruby/actions/runs/3885204101/jobs/6628783596

***YJIT: Printing YJIT statistics on exit***
method call exit reasons: 
               iseq_has_rest     109182 (31.4%)
                 iseq_zsuper      45902 (13.2%)
         args_splat_non_iseq      36456 (10.5%)
                   block_arg      32965 ( 9.5%)
            iseq_arity_error      28523 ( 8.2%)
         iseq_ruby2_keywords      24632 ( 7.1%)
              iseq_has_no_kw      17853 ( 5.1%)
         iseq_splat_with_opt      15316 ( 4.4%)
            args_splat_cfunc      13841 ( 4.0%)
                    kw_splat      12611 ( 3.6%)
    iseq_missing_optional_kw       3942 ( 1.1%)
             iseq_has_kwrest       3229 ( 0.9%)
               iseq_has_post       1958 ( 0.6%)
              refined_method        812 ( 0.2%)
       cfunc_ruby_array_varg        469 ( 0.1%)
                    keywords         90 ( 0.0%)
               zsuper_method          5 ( 0.0%)
invokeblock exit reasons: 
               ifunc       5691 (59.0%)
     iseq_arg0_splat       2000 (20.7%)
                proc       1848 (19.2%)
    iseq_tag_changed         76 ( 0.8%)
              symbol         32 ( 0.3%)
invokesuper exit reasons: 
         block       4716 (99.6%)
    me_changed         21 ( 0.4%)
leave exit reasons: 
        interp_return    1699600 (98.1%)
    start_pc_non_zero      33361 ( 1.9%)
         se_interrupt         26 ( 0.0%)
getblockparamproxy exit reasons: 
    block_param_modified         77 (100.0%)
getinstancevariable exit reasons:
    (all relevant counters are zero)
setinstancevariable exit reasons:
    (all relevant counters are zero)
opt_aref exit reasons: 
    (all relevant counters are zero)
expandarray exit reasons: 
            splat       9944 (100.0%)
    rhs_too_small          4 ( 0.0%)
opt_getinlinecache exit reasons: 
    miss          2 (100.0%)
invalidation reasons: 
          method_lookup        311 (59.9%)
    constant_state_bump        150 (28.9%)
       constant_ic_fill         58 (11.2%)
bindings_allocations:         154
bindings_set:                   0
compiled_iseq_count:         2169
compiled_block_count:       19995
compiled_branch_count:      34907
block_next_count:            7066
defer_count:                 6616
freed_iseq_count:              35
invalidation_count:           519
constant_state_bumps:           0
inline_code_size:         2013729
outlined_code_size:       2011354
freed_code_size:                0
code_region_size:         4030464
yjit_alloc_size:         40456410
live_page_count:              246
freed_page_count:               0
code_gc_count:                  0
num_gc_obj_refs:            15810
object_shape_count:          2408
side_exit_count:           434447
total_exit_count:         2134047
total_insns_count:       88047082
vm_insns_count:           7341642
yjit_insns_count:        81139887
ratio_in_yjit:              91.7%
avg_len_in_yjit:             37.8
Top-19 most frequent exit ops (100.0% of exits):
    opt_send_without_block:     130259 (30.0%)
               invokesuper:     115616 (26.6%)
                      send:     101733 (23.4%)
               invokeblock:      36199 (8.3%)
                  opt_aref:      14027 (3.2%)
                     throw:      10460 (2.4%)
               expandarray:       9948 (2.3%)
             setlocal_WC_0:       8148 (1.9%)
                    opt_eq:       4966 (1.1%)
        getblockparamproxy:       2176 (0.5%)
                 opt_nil_p:        325 (0.1%)
               objtostring:        285 (0.1%)
                checkmatch:        154 (0.0%)
      opt_getconstant_path:         63 (0.0%)
                      once:         58 (0.0%)
                     leave:         26 (0.0%)
               opt_empty_p:          2 (0.0%)
                  opt_ltlt:          1 (0.0%)
          opt_regexpmatch2:          1 (0.0%)
itr #1: 6346ms

@maximecb maximecb merged commit 5bf7218 into ruby:master Jan 10, 2023
@maximecb
Copy link
Contributor

Well done!

@tenderlove tenderlove deleted the special-array branch January 11, 2023 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
4 participants