On Tue, 17 Jan 2023 15:55:40 GMT, Jan Lahoda <[email protected]> wrote:
>> The pattern matching switches are using a bootstrap method
>> `SwitchBootstrap.typeSwitch` to implement the jumps in the switch.
>> Basically, for a switch like:
>>
>> switch (obj) {
>> case String s when s.isEmpty() -> {}
>> case String s -> {}
>> case CharSequence cs -> {}
>> ...
>> }
>>
>>
>> this method will produce a MethodHandle that will be analyze the provided
>> selector value (`obj` in the example), and will return the case index to
>> which the switch should jump. This method also accepts a (re)start index for
>> the search, which is used to handle guards. For example, if the
>> `s.isEmpty()` guard in the above sample returns false, the matching is
>> restarted on the next case.
>>
>> The current implementation is fairly slow, it basically goes through the
>> labels in a loop. The proposal here is to replace that with a MethodHandle
>> structure like this:
>>
>> obj == null ? -1
>> : switch (restartIndex) {
>> case 0 -> obj instanceof String ? 0 : obj instanceof
>> CharSequence ? 2 : ... ;
>> case 1 -> obj instanceof String ? 1 : obj instanceof
>> CharSequence ? 2 : ... ;
>> case 2 -> obj instanceof CharSequence ? 2 : ... ;
>> ...
>> default -> <labels-count>;
>> }
>>
>>
>> This appear to run faster than the current implementation, using testcase
>> similar to the one used for https://github.com/openjdk/jdk/pull/9746 , these
>> are the results
>>
>> PatternsOptimizationTest.testLegacyIndyLongSwitch thrpt 25 1515989.562
>> ± 32047.918 ops/s
>> PatternsOptimizationTest.testHandleIndyLongSwitch thrpt 25 2630707.585
>> ± 37202.210 ops/s
>>
>> PatternsOptimizationTest.testLegacyIndyShortSwitch thrpt 25 6789310.900
>> ± 61921.636 ops/s
>> PatternsOptimizationTest.testHandleIndyShortSwitch thrpt 25 10771729.464
>> ± 69607.467 ops/s
>>
>>
>> The "LegacyIndy" is the current implementation, "HandleIndy" is the one
>> proposed here. The translation in javac used is the one from #9746 in all
>> cases.
>
> Jan Lahoda has updated the pull request with a new target base due to a merge
> or a rebase. The incremental webrev excludes the unrelated changes brought in
> by the merge/rebase. The pull request contains four additional commits since
> the last revision:
>
> - Adding comments
> - Improving performance
> - Merge branch 'master' into JDK-8291966
> - 8291966: SwitchBootstrap.typeSwitch could be faster
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 70:
> 68: try {
> 69: INSTANCEOF_CHECK = LOOKUP.findStatic(SwitchBootstraps.class,
> "instanceofCheck",
> 70:
> MethodType.methodType(boolean.class, Object.class, Class.class));
I am tempted to have
Suggestion:
INSTANCEOF_CHECK =
MethodHandles.permuteArguments(LOOKUP.findVirtual(Class.class, "isInstance",
MethodType.methodType(boolean.class,
Object.class)), MethodType.methodType(boolean.class, Object.class,
Class.class), 1, 0);
to reduce the implementation methods in SwitchBootstraps
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 75:
> 73: OBJECT_EQ_CHECK = LOOKUP.findStatic(Objects.class, "equals",
> 74:
> MethodType.methodType(boolean.class, Object.class, Object.class));
> 75: NULL_CHECK = LOOKUP.findStatic(SwitchBootstraps.class,
> "nullCheck",
Suggestion:
NULL_CHECK = LOOKUP.findStatic(Objects.class, "isNull",
Can remove the `nullCheck` method.
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 79:
> 77: IS_ZERO = LOOKUP.findStatic(SwitchBootstraps.class, "isZero",
> 78:
> MethodType.methodType(boolean.class, int.class));
> 79: ENUM_LOOKUP = LOOKUP.findStatic(SwitchBootstraps.class,
> "enumLookup",
Appears unused and should be removed.
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 175:
> 173: MethodHandle def =
> MethodHandles.dropArguments(MethodHandles.constant(int.class, labels.length),
> 0, Object.class);
> 174: MethodHandle[] testChains = new MethodHandle[labels.length];
> 175: List<Object> labelsList = new ArrayList<>(Arrays.asList(labels));
Suggestion:
List<Object> labelsList = Arrays.asList(labels).reversed();
Requires you to update to latest JDK with the SequencedCollection patch; labels
is already sanitized upon bootstrap method call, no need to copy again since
you don't modify it
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 177:
> 175: List<Object> labelsList = new ArrayList<>(Arrays.asList(labels));
> 176:
> 177: Collections.reverse(labelsList);
Suggestion:
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 321:
> 319: if (constantsOnly) {
> 320: long nonNullValues = Stream.of(labels).filter(l -> l !=
> null).count();
> 321: long distinctNonNullValues = Stream.of(labels).filter(l -> l
> != null).distinct().count();
Suggestion:
long nonNullValues =
Stream.of(labels).filter(Objects::nonNull).count();
long distinctNonNullValues =
Stream.of(labels).filter(Objects::nonNull).distinct().count();
src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java line 337:
> 335: // };
> 336: //else return "createRepeatIndexSwitch(labels)"
> 337: MethodHandle[] map = new
> MethodHandle[enumClass.getEnumConstants().length];
Can use JavaLangAccess.getEnumConstantsShared() to get the shared array to
avoid copying; same below in the for loop.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181294780
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181294053
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181294384
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181293988
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181293296
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181293602
PR Review Comment: https://git.openjdk.org/jdk/pull/9779#discussion_r1181278511