FTR: JVM Lang Summit presentation

Paul Sandoz paul.sandoz at oracle.com
Wed Sep 3 14:44:21 UTC 2014


On Sep 2, 2014, at 8:02 PM, Remi Forax <forax at univ-mlv.fr> wrote:
> back from vacation too,
> slowly trying to anwser to a pile of emails ...
> 

I know how you feel :-)


> I was to answer yes, but I think the code can be modified a little to avoid the invoke() and use invokeExact instead
> so what about:
> 
> private static MethodHandle compareAndSet(Lookup lookup, Class<?> declaringClass, String fieldName, Class<?> fieldType)
>       throws NoSuchFieldException, IllegalAccessException {
>   MethodHandle getter = lookup.findGetter(declaringClass, fieldName, fieldType);
>   MethodHandleInfo methodHandleInfo = lookup.revealDirect(getter);
>   Field field = methodHandleInfo.reflectAs(Field.class, lookup);
>   long offset = UNSAFE.objectFieldOffset(field);
>   MethodHandle mh = MethodHandles.insertArguments(COMPARE_AND_SWAP_OBJECT, 1, offset);
>   return mh.asType(MethodType.methodType(boolean.class, declaringClass, fieldType, fieldType))
>             asType(MethodType.methodType(boolea,.class, Object.class, Object.class, Object.class));
> }
> 
> public class VarHandle<T, U> {
>   private final @Stable MethodHandle compareAndSet;
> 
>   VarHandle(MethodHandle compareAndSet) {
>       this.compareAndSet = compareAndSet;
>   }
> 
>   public boolean compareAndSet(T object, U expected, U value) {
>     try {
>       return (boolean)compareAndSet.invokeExact(object, expected, value);
>     } catch (Throwable e) {
>       if (e instanceof RuntimeException) throw (RuntimeException)e;
>       if (e instanceof Error) throw (Error)e;
>       throw new AssertionError(e);
>     }
>   }
> }
> 
> if the VarHandle is stored in a static final, the JIT should be able to remove the downcast/upcast pair.
> 

Thanks, that's a rather neat trick! 

It triggered the following thought, why not just do (assuming identical access control checks on construction of the handle):

    public boolean compareAndSet_direct(R r, V e, V v) {
        r.getClass(); // check for null
        MethodHandleImpl.castReference(rType, r);
        MethodHandleImpl.castReference(vType, e);
        MethodHandleImpl.castReference(vType, v);
        return MethodHandleStatics.UNSAFE.compareAndSwapObject(r, v_offset, e, v);
    }

There is no class spinning and the inline trace is much smaller (although much could be reduced with specific D-like MHs).

I experimented a bit with both of the above (see end of email for code) and each gets really rather close perf-wise and assembly-wise. I never quite realized how aggressive the JIT is at optimizing this area. Those casts can get reduced to almost no-ops (like the possibly redundant cast of the value in accessor DHMs). There are some redundant null-checks, as previously discussed on core-libs.

Are those casts always guaranteed to get optimized away if the handle is static final and an invocation on that handle is inlined?


>> 
>> Any replacement of Unsafe in j.u.concurrent classes has to be almost, if not, as performant as that of direct Unsafe usage. It's proving quite useful to have something that works along similar fundamental lines (w.r.t. method invocation) that already gets quite close to Unsafe performance and from which we can evaluate and improve certain aspects.
> 
> Anyway, i still think that the best way to write a safe compareAndSet is not to use a Varandle or any polymorphic signature method, but to let the compiler generate an invokedynamic with the right signature. The bootstrap method will verify the signature once (at link time) and so the generated method handle will only do downcasts to j.l.Object, something that the JIT will trivially consider as a no-op.
> 

I consider that as possible further steps, requiring some syntax bike shedding and a mechanism for how javac produces the indy call. Regardless we still need to evaluate the low-level plumbing.

Paul.




More information about the valhalla-dev mailing list