<div dir="ltr">Hello Jochen and Claes,<div>I have done a little debugging and have found the cause, that looking up pre-generated LambdaForm (mentioned by Claes) causes VM to initialize an NoSuchMethodError [1] that's later silently dropped [2], but the NoSuchMethodError constructor is already executed and the stacktrace filled, causing a significant overhead, as shown in this [3] JMC's rendering of a JFR recording.</div><div><br></div><div>You can capture this NoSuchMethodError construction with IDE debug, even when running an empty main, on newer JDK versions. I tested with a breakpoint in NoSuchMethodError(String) constructor and it hits twice (for instrumentation agent uses reflection, which now depends on Method Handles after JEP 416 in Java 18).</div><div><br></div><div>I think a resolution would be to modify linkResolver so that it can also resolve speculatively instead of always throwing exceptions, but this might be too invasive and I want to hear from other developers such as Claes, who authored the old resolveOrNull silent-dropping patch.</div><div><br></div><div>Looking forward to a solution,</div><div>Chen Liang</div><div><br></div><div>[1]: <a href="https://github.com/openjdk/jdk/blob/a04c6c1ac663a1eab7d45913940cb6ac0af2c11c/src/hotspot/share/interpreter/linkResolver.cpp#L773">https://github.com/openjdk/jdk/blob/a04c6c1ac663a1eab7d45913940cb6ac0af2c11c/src/hotspot/share/interpreter/linkResolver.cpp#L773</a><br></div><div>[2]: <a href="https://github.com/openjdk/jdk/blob/a04c6c1ac663a1eab7d45913940cb6ac0af2c11c/src/hotspot/share/prims/methodHandles.cpp#L794-L796">https://github.com/openjdk/jdk/blob/a04c6c1ac663a1eab7d45913940cb6ac0af2c11c/src/hotspot/share/prims/methodHandles.cpp#L794-L796</a><br></div><div>[3]: <a href="https://cr.openjdk.org/~liach/mess/invokerbytecodegen-cache-miss.png">https://cr.openjdk.org/~liach/mess/invokerbytecodegen-cache-miss.png</a><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Sep 11, 2023 at 4:41 PM Jochen Theodorou <<a href="mailto:blackdrag@gmx.org">blackdrag@gmx.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I changed my testing a bit to have more infrastructure types and test<br>
with a fresh VM each time.<br>
<br>
The scenario is still the same: call a method foo with argument 1. foo<br>
does nothing but returning 0. Implement the call.<br>
<br>
indyDirect:<br>
bootstrap method selects method and produces constant call-site<br>
<br>
indyDoubleDispatch:<br>
bootstrap selects a selector method and produces a mutable call-site.<br>
selector then selects the target method and sets it in the call-site<br>
<br>
reflective:<br>
a inner class is used to select the method using reflection and directly<br>
invoke it.<br>
<br>
reflectiveCached:<br>
same as reflective but caching the selected method<br>
<br>
staticCallSite:<br>
I have the call abstracted and replace what is called after method<br>
selection. Here with a direct call to the method using normal Java<br>
<br>
runtimeCallSite:<br>
I have the call abstracted like staticCallSite, but instead of replacing<br>
with a direct call I create a class at runtime, which does the direct<br>
call for me.<br>
<br>
My interest is in the performance of the first few calls. My experiments<br>
show that at most 5 calls there is no significant performance change<br>
anymore for a long time. But long time performance is secondary right now.<br>
<br>
Out of these implementations it is no surprise that staticCallSite has<br>
the least cost, but it is almost on par with the reflective variant.<br>
That really surprised me. It seems reflection came a long way since the<br>
old times. There is probably still a lot of cost in the long term, but<br>
well, I focus on the short term here right now.<br>
<br>
The cached variant really differs not much but if reflection gets a<br>
score of 41, then the cached variant is at 105. That is surprising much<br>
for an additional if condition. But if you think of how many<br>
instructions that involves maybe not that surprising. indyDirect has<br>
almost the same initial cost as the reflectiveCached. indyDoubleDispatch<br>
follows with a score of 149... which looks very much like<br>
reflective+indyDirect-"a small something". At 361 we find<br>
runtimeCallSite, the slowest by far. The numbers used to be quite<br>
different for this, but back then MagicAccessor was an option to reduce<br>
cost.<br>
<br>
My conclusion so far. callsite generation is a questionable option. Not<br>
only because of performance, but also because of the module system.<br>
Though we have cases where we can use the static variant.<br>
<br>
The next best is actually reflective. But how would you combine<br>
reflective with something that has better long term performance? Even a<br>
direct call with indy costs much more.<br>
<br>
I think I have to change my tests.. I think I should test a scenario in<br>
which I have a quite big number - like 1 million - of one-time<br>
call-sites to get really conclusive numbers... Of course that means 1<br>
million direct method handles for indy.<br>
<br>
Well, I will write again if I have more numbers.<br>
<br>
bye Jochen<br>
_______________________________________________<br>
mlvm-dev mailing list<br>
<a href="mailto:mlvm-dev@openjdk.org" target="_blank">mlvm-dev@openjdk.org</a><br>
<a href="https://mail.openjdk.org/mailman/listinfo/mlvm-dev" rel="noreferrer" target="_blank">https://mail.openjdk.org/mailman/listinfo/mlvm-dev</a><br>
</blockquote></div>