[intrinsics] performance improvements for the intrinsified version of Objects::hash
Vicente Romero
vicente.romero at oracle.com
Thu Feb 28 00:23:12 UTC 2019
On 2/27/19 4:22 PM, Brian Goetz wrote:
> We are delegating hashing to a method:
>
> int hash(int[] numbers)
>
> which means that while we may be eliminating primitive boxing, we are
> still paying for array boxing. Using either MH loop combinators or
> hand-unrolled variants of the above should unlock more speedup on the
> small (and most common) cases.
I have develop a version with loop combinators, please find the patch
attached, but I didn't push it as performance numbers are worst than
with the current version. It could be that unrolling some variants could
speed up small cases but we are already beating vanilla JDK for those cases
>
> On 2/27/2019 3:24 PM, Alex Buckley wrote:
>> On 2/26/2019 5:49 PM, Vicente Romero wrote:
>>> In the last performance measurement we found a noticeable
>>> degradation in performance for large number of arguments (~100), even
>>> for primitive types. Patch [1] improves the performance for both
>>> primitive and reference types with the difference that now the
>>> performance is much better than vanilla JDK13 for primitive types
>>> but it
>>> is still worst than vanilla for reference types. Although we are in
>>> better shape now compared to the state as of 02/22. Keep tuned :)
>>
>> Previous intrinsification effort, relative to vanilla JDK 13:
>>
>> Intrinsified Vanilla Speedup
>> testHash1IntVariable 42564 42799 1x
>> testHash2IntVariables 41573 9019 5x
>> testHash100IntVariables 4 27 0.15x
>>
>> New intrinsification effort, relative to vanilla JDK 13:
>>
>> Intrinsified Vanilla Speedup
>> testHash1IntVariable 41149 42799 1x
>> testHash2IntVariables 19075 9019 2x
>> testHash100IntVariables 697 27 26x
>>
>> I note that the speedup of the 2IntVariables case is cut from 5x to
>> 2x. That seems like quite a penalty for speeding up the
>> 100IntVariables case (admittedly by a lot). But maybe what's
>> happening is that the speedup improves as more variables are hashed.
>> I wonder if it's fair to say for Objects::hash that vanilla
>> invocation has a high fixed cost (box box box) and low variable costs
>> (but who cares, because they're overwhelmed by the fixed cost) ...
>> while intrinsified invocation has a low fixed cost (just run the BSM)
>> but higher variable costs -- you pay for the hashing you get.
>>
>> Alex
>
-------------- next part --------------
# HG changeset patch
# User vromero
# Date 1551310897 18000
# Wed Feb 27 18:41:37 2019 -0500
# Branch intrinsics-project
# Node ID 1e6d5ad1c08676a89734841fdc94b25b0da79e9b
# Parent 9e8c3034e18a35ab97ead9e422c13f0ad08d0dd7
using loop combinators for the intrinsified version of Objects::hash
diff -r 9e8c3034e18a -r 1e6d5ad1c086 src/java.base/share/classes/java/lang/invoke/ObjectsBootstraps.java
--- a/src/java.base/share/classes/java/lang/invoke/ObjectsBootstraps.java Wed Feb 27 13:27:35 2019 -0500
+++ b/src/java.base/share/classes/java/lang/invoke/ObjectsBootstraps.java Wed Feb 27 18:41:37 2019 -0500
@@ -33,6 +33,7 @@
import java.util.Map;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.lang.invoke.MethodType.methodType;
/**
* Bootstrapping support for Objects intrinsics.
@@ -53,9 +54,11 @@
return new ConstantCallSite(createHashMethodHandle(methodType.parameterArray()));
}
+ static final Class<ObjectsBootstraps> OBJECTS_BOOTSTRAPS_CLASS = ObjectsBootstraps.class;
static Map<Class<?>, MethodHandle> HASH_METHODS;
static MethodHandle HASH_OBJECT;
- static MethodHandle ALL_HASHES_MH;
+ static final MethodType MT_loopBody = methodType(int.class, int.class, int.class, int[].class);;
+ static MethodHandle MH_loopBody;
static void initialize() {
if (HASH_METHODS == null) {
@@ -70,20 +73,24 @@
HASH_OBJECT = findHashMethod(Object.class);
try {
- ALL_HASHES_MH = IMPL_LOOKUP.findStatic(ObjectsBootstraps.class, "combineHashes", MethodType.methodType(int.class, int[].class));
+ MH_loopBody = IMPL_LOOKUP.findStatic(OBJECTS_BOOTSTRAPS_CLASS, "loopBody", MT_loopBody);
} catch (NoSuchMethodException | IllegalAccessException ex) {
-
+ // debug
+ System.out.println(ex);
}
}
}
static MethodHandle createHashMethodHandle(Class<?>... argTypes) {
+ MethodHandle iterations = MethodHandles.dropArguments(MethodHandles.constant(int.class, argTypes.length), 0, int[].class);
+ MethodHandle start = MethodHandles.dropArguments(MethodHandles.constant(int.class, 1), 0, int[].class);
+ MethodHandle mhHash = MethodHandles.countedLoop(iterations, start, MH_loopBody).asVarargsCollector(int[].class);
Class<?>[] intArgs = new Class<?>[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
intArgs[i] = int.class;
}
MethodType methodType = MethodType.methodType(int.class, intArgs);
- MethodHandle mhHash = ALL_HASHES_MH.asType(methodType);
+ mhHash = mhHash.asType(methodType);
MethodHandle[] filters = new MethodHandle[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
filters[i] = getHashMethod(argTypes[i]);
@@ -92,30 +99,23 @@
return mhHash;
}
- static int combineHashes(int... hashes) {
- if (hashes == null) {
- return 0;
- }
- int result = 1;
- for (int hash : hashes) {
- result = 31 * result + hash;
- }
- return result;
+ static int loopBody(int result, int counter, int[] a) {
+ return 31 * result + a[counter];
}
static MethodHandle getHashMethod(Class<?> type) {
MethodHandle hashMH = HASH_METHODS.get(type);
if (hashMH == null) {
hashMH = HASH_OBJECT;
- hashMH = hashMH.asType(MethodType.methodType(int.class, type));
+ hashMH = hashMH.asType(methodType(int.class, type));
}
return hashMH;
}
static MethodHandle findHashMethod(Class<?> cls) {
try {
- MethodType mt = MethodType.methodType(int.class, cls);
- return IMPL_LOOKUP.findStatic(ObjectsBootstraps.class, "hash", mt);
+ MethodType mt = methodType(int.class, cls);
+ return IMPL_LOOKUP.findStatic(OBJECTS_BOOTSTRAPS_CLASS, "hash", mt);
} catch (NoSuchMethodException | IllegalAccessException ex) {
return null;
}
More information about the amber-dev
mailing list