[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