ConstantDynamic in unvisited catch causes 74X slowdown
Paul Sandoz
paul.sandoz at oracle.com
Tue Jan 25 20:58:41 UTC 2022
I think the compiler is bailing with a complex try body. When there is a finally block the compiler will need to analyze all the non-exceptional return points and duplicate the finally block code.
I need to look in more detail how you are defining the try/catch/block… since as you say the LDC occurs on exception cases, but you use a null value for the exception (also need to remind myself of the bytecode).
Try this:
public static class Cliff implements Climbable {
public int climb(int input) {
if(input == 0) {
return ++input;
} else if(input > 1) {
return --input;
} else {
return loop();
}
}
private int loop() {
long l = 0;
for(int i = 0; i < 100_000; i++) { // 7
l+=getaLong(l); // 8
}
return (int) l; // 9
}
private long getaLong(long l) {
return l + 5;
}
}
Paul.
> On Jan 25, 2022, at 12:26 PM, Eirik Bjørsnøs <eirbjo at gmail.com> wrote:
>
> Remi,
>
> The catch block is actually only reachable via exception, otherwise the
> method returns as normally. There is no GOTO.
>
> If I also LDC the same ConstantDynamic in the main part of the method
> (outside the catch block), then indeed the method gets compiled.
>
> I don't actually want try/finally semantics here, since the instrumentation
> logic on the catch branch should be different than when the method exits
> normally. So the semantics here is more try/catch/rethrow.
>
> Thanks,
> Eirik.
>
> On Tue, Jan 25, 2022 at 9:00 PM Remi Forax <forax at univ-mlv.fr> wrote:
>
>> Hi,
>> if i'm not wrong in the code you generate the catch block can be reached
>> either by the normal case (no goto in the normal case) and if there is an
>> exception,
>> usually JITs bail out when they detect this kind of patterns.
>>
>> There is a simple way to generate efficient bytecodes, generate exactly
>> the same code as javac :)
>> In your case, you want to generate an equivalent of a try/finally, so the
>> invokedynamic/ldc constantdynamic should be generated twice, one in the
>> normal case and one in the exceptional case with a goto after the normal
>> case to jump over the exception block.
>>
>> regards,
>> Rémi
>>
>> ----- Original Message -----
>>> From: "eirbjo" <eirbjo at gmail.com>
>>> To: "hotspot compiler" <hotspot-compiler-dev at openjdk.java.net>
>>> Sent: Tuesday, January 25, 2022 8:19:27 PM
>>> Subject: ConstantDynamic in unvisited catch causes 74X slowdown
>>
>>> Hi,
>>>
>>> I'm looking at a JMH benchmark which shows a 74X slowdown when a
>>> single invokeDynamic instruction is replaced by an equivalent
>>> ConstantDynamic LDC instruction.
>>>
>>> An interesting side note is that the LDC/invokeDynamic instructions live
>>> inside a catch block which wraps the method being run. So the
>>> LDC/invokeDynamic instructions are actually never executed when running
>> the
>>> benchmark (because no exception was thrown/catched).
>>>
>>> I was expecting ConstantDynamic to be as fast as (or faster than)
>>> invokeDynamic, so this one is a bit of a puzzle. Does it make sense that
>>> simply having a ConstantDynamic instruction present in the code prevents
>>> some Hotspot optimizations from kicking in?
>>>
>>> I've made a Maven project [1] with a minimal reproducer [2] for the
>> issue.
>>> (Minimal does not mean small, since there is a good amount of code for
>>> setting up the instrumentation and class loading magic)
>>>
>>> With Java 17.0.1 on my Intel Macbook pro, the JMH benchmark runs at ~350
>>> ops/s using ConstantDynamic, and around 27000 ops/s when switching to
>>> invokeDynamic.
>>>
>>> What is going on here?
>>>
>>> [1] https://github.com/eirbjo/constant-dynamic-cliff
>>> [2]
>>>
>> https://github.com/eirbjo/constant-dynamic-cliff/blob/main/src/test/java/com/eirbjo/cpc/ConstantDynamicCliff.java
>>>
>>> Thanks,
>>> Eirik.
>>
More information about the hotspot-compiler-dev
mailing list