[9] RFR (M): 8027827: Improve performance of catchException combinator
Vladimir Ivanov
vladimir.x.ivanov at oracle.com
Thu Feb 20 09:57:31 PST 2014
Paul,
Thanks for the feedback! See my answers inline.
Updated webrev:
http://cr.openjdk.java.net/~vlivanov/8027827/final/webrev.01/
I finally figured out how to make caching work. This webrev contains
these changes.
I changed LF representation a bit and added 2 auxiliary method handles
- argument boxing and wrapping into Object[] and result unboxing. These
operations depend on actual type and can't be shared among arbitrary
combinators with the same basic type. They are used only during LF
interpretation and are completely ignored in compiled LFs.
On 2/20/14 7:51 PM, Paul Sandoz wrote:
> Hi Vladimir,
>
> I know just enough about this area to be dangerous....
>
>
> src/share/classes/java/lang/invoke/BoundMethodHandle.java
>
> 865 private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[4];
>
>
> Only 3 elements are stored in the above array?
Fixed.
>
>
>
> src/share/classes/java/lang/invoke/MethodHandleImpl.java
>
> 634 // t_{i+2}:L=ValueConversions.unbox(t_{i+1}) OR ValueConversions.identity(t_{i+1})
> 635 if (type.returnType().isPrimitive()) {
> 636 names[UNBOX_RESULT] = new Name(ValueConversions.unbox(type.returnType()),
> 637 names[TRY_CATCH]);
> 638 } else {
> 639 names[UNBOX_RESULT] = new Name(ValueConversions.identity(),
> 640 names[TRY_CATCH]);
> 641 }
>
>
> You could create the form without the identity transform for the non-primitive case?
>
> final int UNBOX_RESULT = type.returnType().isPrimitive() ? nameCursor++ : 0;
> ...
>
> if (UNBOX_RESULT > 0) { ...
> names[UNBOX_RESULT] = new Name(ValueConversions.unbox(type.returnType()), names[TRY_CATCH]);
> }
I can, but it complicates matching and compiling the pattern in
InvokerBytecodeGenerator. I decided to keep the shape uniform for all cases.
>
>
> 699 try {
> 700 GUARD_WITH_CATCH
> 701 = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "guardWithCatch",
> 702 MethodType.methodType(Object.class, MethodHandle.class, Class.class, MethodHandle.class, Object[].class));
> 703 } catch (ReflectiveOperationException ex) {
> 704 throw new RuntimeException(ex);
> 705 }
> 706 return GUARD_WITH_CATCH;
>
>
> Should #704 be:
>
> throw newInternalError(ex);
>
> ?
Fixed. Also updated other places where RuntimeException was thrown.
>
>
> test/java/lang/invoke/MethodHandles/TestCatchException.java
>
> 100 Object o = new Object();
> 101 Object[] obj1 = new Object[] { "str" };
> 102
> 103 Object r1 = target.invokeExact(o, o, o, o, o, o, o, o, obj1);
> 104 Object r2 = gwc.invokeExact(o, o, o, o, o, o, o, o, obj1);
> 105 assertTrue(r1 != null);
> 106 assertTrue(r1.equals(r2));
>
> To be on the safe side should probably assert as follows:
>
> assertEquals(r1, obj);
> assertEquals(r2, obj);
Fixed.
Best regards,
Vladimir Ivanov
> Paul.
>
>
> On Feb 19, 2014, at 10:46 PM, Vladimir Ivanov <vladimir.x.ivanov at oracle.com> wrote:
>
>> http://cr.openjdk.java.net/~vlivanov/8027827/final/webrev.00
>> https://jbs.oracle.com/bugs/browse/JDK-8027827
>> 354 lines changed: 193 ins; 91 del; 70 mod
>>
>> OVERVIEW
>>
>> MethodHandles.catchException combinator implementation is based on generic invokers (MethodHandleImpl$GuardWithCatch.invoke_*). It is significantly slower than a Java equivalent (try-catch).
>>
>> Future Nashorn performance improvements require catchException combinator speed to be on par with try-catch in Java.
>>
>> So, it should be represented in a more efficient form.
>>
>> I chose the following lambda form representation:
>>
>> t_{i}:L=ValueConversions.array(a1:L,...,a_{k}:L);
>> t_{i+1}:L=MethodHandleImpl.guardWithCatch(t_{p1}, t_{p2}, t_{p3}, t_{i}:L);
>> t_{i+2}:I=ValueConversions.unbox(t7:L);
>> OR :L=ValueConversions.identity(t_{n+1})
>> OR :V=ValueConversions.ignore(t_{n+1})
>>
>> where:
>> a1, ..., a_{k} - arguments
>> t_{p1}, t_{p2}, t_{p3} - target method handle, exception class, catcher method handle respectively; passed as bounded parameters;
>>
>> During lambda form compilation it is converted into bytecode equivalent of the following Java code:
>> try {
>> return target.invokeBasic(...);
>> } catch(Throwable e) {
>> if (!exClass.isInstance(e)) throw e;
>> return catcher.invokeBasic(e, ...);
>> }
>>
>> There's a set of microbenchmarks (attached to the bug) I wrote to verify performance characteristics of new implementation.
>>
>> FURTHER WORK
>>
>> What is missing is lambda form caching. The plan is to have a single lambda form per basic type, but it needs more work - current representation is suitable for sharing on bytecode level, but lambda form interpretation doesn't work well (arguments boxing + result unboxing are problematic).
>>
>> TESTING
>>
>> Tests: microbenchmarks, jdk/java/lang/invoke/, nashorn with optimistic types (unit tests, octane).
>>
>> Tested in 2 modes:
>> * COMPILE_THRESHOLD=30
>> * COMPILE_THRESHOLD=0 -Xverify:all
>>
>> OTHER
>>
>> 1) Update of cached name and member in LF compilation loop (see InvokerBytecodeGenerator.generateCustomizedCodeBytes) fixes a bug during compilation of selectAlternative when running with COMPILE_THRESHOLD=0.
>>
>> 2) As part of this change, I fix existing bug [1], so I add regression test for it.
>>
>> Thanks!
>>
>> Best regards,
>> Vladimir Ivanov
>>
>> [1] https://bugs.openjdk.java.net/browse/JDK-8034120
>
More information about the mlvm-dev
mailing list