On Wed, 26 Jun 2024 12:46:18 GMT, Claes Redestad <[email protected]> wrote:
>> For general pattern matching switches, the `SwitchBootstraps` class
>> currently generates a cascade of `if`-like statements, computing the correct
>> target case index for the given input.
>>
>> There is one special case which permits a relatively easy faster handling,
>> and that is when all the case labels case enum constants (but the switch is
>> still a "pattern matching" switch, as tranditional enum switches do not go
>> through `SwitchBootstraps`). Like:
>>
>>
>> enum E {A, B, C}
>> E e = ...;
>> switch (e) {
>> case null -> {}
>> case A a -> {}
>> case C c -> {}
>> case B b -> {}
>> }
>>
>>
>> We can create an array mapping the runtime ordinal to the appropriate case
>> number, which is somewhat similar to what javac is doing for ordinary
>> switches over enums.
>>
>> The `SwitchBootstraps` class was trying to do so, when the restart index is
>> zero, but failed to do so properly, so that code is not used (and does not
>> actually work properly).
>>
>> This patch is trying to fix that - when all the case labels are enum
>> constants, an array mapping the runtime enum ordinals to the case number
>> will be created (lazily), for restart index == 0. And this map will then be
>> used to quickly produce results for the given input. E.g. for the case
>> above, the mapping will be `{0 -> 0, 1 -> 2, 2 -> 1}` (meaning `{A -> 0, B
>> -> 2, C -> 1}`).
>>
>> When the restart index is != 0 (i.e. when there's a guard in the switch, and
>> the guard returned `false`), the if cascade will be generated lazily and
>> used, as in the general case. If it would turn out there are significant
>> enum-only switches with guards/restart index != 0, we could improve there as
>> well, by generating separate mappings for every (used) restart index.
>>
>> I believe the current tests cover the code functionally sufficiently - see
>> `SwitchBootstrapsTest.testEnums`. It is only that the tests do not (and
>> regression tests cannot easily, I think) differentiate whether the
>> special-case or generic implementation is used.
>>
>> I've added a new microbenchmark attempting to demonstrate the difference.
>> There are two benchmarks, both having only enum constants as case labels.
>> One, `enumSwitchTraditional` is an "old" switch, desugared fully by javac,
>> the other, `enumSwitchWithBootstrap` is an equivalent switch that uses the
>> `SwitchBootstraps`. Before this patch, I was getting values like:
>>
>> Benchmark Mode Cnt Score Error Units
>> SwitchEnum.enumSwitchTraditional avgt 15 11.719 ± 0.333 ns/op
>> Swi...
>
> test/micro/org/openjdk/bench/java/lang/runtime/SwitchEnum.java line 57:
>
>> 55: for (E e : inputs) {
>> 56: sum += switch (e) {
>> 57: case null -> -1;
>
> As this `null` case adds a case relative to the `-Traditional` test then
> maybe removing one of the `E0, E1, ...` cases would make the test a little
> bit more apples-to-apples?
Using `case null -> ` will push javac to use the new code, but all switches do
some kind of null check for the selector value. The difference is that if
there's no `case null`, there will be `Objects.requireNonNull` generated for
the selector value (which will throw an NPE if the value is `null`), while here
it will jump to the given case.
So, `case null` does not have the same weight as a normal case, so I don't
think it would be fair to remove a full case to compensate for it.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/19906#discussion_r1654784712