JDK-8160821 : VarHandle accesses are penalized when argument conversion is required
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 8,22
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2016-07-05
  • Updated: 2023-07-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
This looks similar to JDK-8153870, but for arguments.

See the test:

    private static final VarHandle VH_LONG = MethodHandles.byteBufferViewVarHandle(
            long[].class, java.nio.ByteOrder.nativeOrder());

    ByteBuffer buffer;

    @Setup
    public void setup() {
        buffer = ByteBuffer.allocateDirect(3 * 1024).alignedSlice(8);
    }

    @Benchmark
    public void test_int() {
        for (int i = 0; i < 100; i++) {
            VH_LONG.set(buffer, i, i);
        }
    }

    @Benchmark
    public void test_long() {
        for (int i = 0; i < 100; i++) {
            VH_LONG.set(buffer, i, (long) i);
        }
    }

Benchmark                 Mode  Cnt     Score   Error  Units
VHLongMismatch.test_int   avgt    5  1370.022 ± 0.350  ns/op
VHLongMismatch.test_long  avgt    5    41.471 ± 0.537  ns/op

33x performance hit.

Tests:
  http://cr.openjdk.java.net/~shade/8160821/VHLongMismatch.java
  http://cr.openjdk.java.net/~shade/8160821/benchmarks.jar
  
Comments
The problem is that the symbolic type descriptor (at the call site) does not match one of the fast paths, and so invocation resorts to the following to the else block in, say, the following: @ForceInline @LambdaForm.Compiled final static void guard_LL_V(VarHandle handle, Object arg0, Object arg1, VarHandle.AccessDescriptor ad) throws Throwable { if (handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); } else if (handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle, arg0, arg1); } } The MethodHandle returned from handle.getMethodHandle(ad.mode) is cached in VarHandle in an @Stable structure. The problem is with the asType transformation: mh.asType(ad.symbolicMethodTypeInvoker) which produces a non-constant (although cached for the same method type on subsequent calls) MethodHandle. Such MethodHandles produced by asType are tricky to cache as constants since a mapping from source to target type is required. Perhaps one way to solve this (although i do not know how to go about it) is to relink the call site to a special MethodHandle linker whose appendix argument is the MethodHandle returned by asType.
05-07-2016