You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ZJIT: Compile opt_case_dispatch instead of the === chain
`opt_case_dispatch` looks a `case` value up in a compile-time hash and jumps
straight to the matching `when` body. ZJIT popped the key and fell through to
the `===` chain the compiler emits after it, which costs one `Integer#===`
send per `when` clause tested.
Worse, the interpreter never executes that chain -- it jumps out of
`opt_case_dispatch` on a hit -- so none of those sends are ever profiled.
Every one of them compiled to an unprofiled, un-inlined `CCallWithFrame` to
`Integer#===`. On the rubyboy benchmark that was 840M cfunc calls, 90% of all
calls to non-inlined C methods and 63% of all calls to C from JIT code, for a
`case addr >> 12` bus decoder and a ~500-way CPU opcode dispatch.
Compile the lookup instead. When every key in the dispatch hash is a Fixnum,
emit a binary search over the sorted keys that branches directly to each
`when` body, guarded by a `BOP_EQQ`/Integer patch point (the chain calls
`Integer#===`, so the lookup only agrees with it while that is the stock
implementation). Keys that are not Fixnums take the `===` chain as before, so
behavior is unchanged for them, and hashes with non-Fixnum keys are left
alone entirely.
Also annotate `Integer#===`, which is `rb_int_equal` just like `Integer#==`,
so the `===` chains that remain inline their comparisons.
A 256-way `case` over integer literals goes from 1816ms to 150ms (YJIT: 308ms).
On rubyboy, calls to C from JIT code drop from 1.32B to 909M and the total
executed instruction count drops 27%.
0 commit comments