[ 
https://issues.apache.org/jira/browse/GROOVY-12325?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18110922#comment-18110922
 ] 

ASF GitHub Bot commented on GROOVY-12325:
-----------------------------------------

blackdrag commented on PR #2852:
URL: https://github.com/apache/groovy/pull/2852#issuecomment-5519131768

   > On real applications: the numbers we have are still micros 
(`CachedMethod.invoke` in isolation, plus a short megamorphic row). They do not 
stand in for Grails, Gradle, or a long-lived Groovy service. I would not claim 
a wall-clock win there without a workload that keeps the same `MetaClass` 
method table hot. If you have a candidate app bench, I am happy to run 
`disable=true` against the default on it. The kill switch stays for anyone who 
would rather not generate.
   
   @daniellansun the problem I have is simple, what kind of program would use 
this path so extensively? If we talk about a long running program the threshold 
is reached, but it does not mean we are talking about a hot path. I guess that 
means we have to look for a tight loop, and the call must be dynamic from the 
Java side to an existing method, instead of calling the method directly. That 
means paths that go maybe through GroovyObject.invoke, InvokerHelper, MetaClass 
or CachedMethod itself. In Groovy itself we use most of these somewhere in 
extension methods usually. 
   
   Maybe DynamicDispatchBench, especially the ones using MethodInterceptor 
could answer the question better. But my expectation is that the surrounding 
machinery shadows the actual call cost.  
   We could also create another benchmark around ObjectRange, basically use the 
for-loop to iterate through it and cause a lot of dynamic "next" calls from 
Java world.
   
   Of course those are not real applications and just give indicators




> Speed up CachedMethod.invoke with a generated JIT-constant trampoline
> ---------------------------------------------------------------------
>
>                 Key: GROOVY-12325
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12325
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Daniel Sun
>            Priority: Major
>
> h2. Problem
> {{CachedMethod.invoke}} is the MOP/Java fallback used by {{MetaClassImpl}}, 
> classic uncompiled call sites, and the default indy cold tier 
> ({{invokeColdReflective}} -> {{doMethodInvoke}}).
> That path still calls {{java.lang.reflect.Method.invoke}}. A {{MethodHandle}} 
> held in an instance field is in the same performance band. After C2, only a 
> JIT-constant callee (direct {{invokevirtual}} / {{invokestatic}} / 
> {{invokeinterface}} in generated bytecode, or {{invokeExact}} of a {{static 
> final}} / classData handle, or a linked {{invokedynamic}} CallSite) runs like 
> a Java direct call.
> Hot monomorphic indy and {{@CompileStatic}} already have that shape. 
> {{CachedMethod.invoke}} does not.
> h2. Approach
> After {{groovy.cachedmethod.invoker.threshold}} hits (default 100, below 
> {{groovy.indy.optimize.threshold}} of 1000 so cold indy is still on 
> {{doMethodInvoke}} when the trampoline appears), install a generated 
> {{DirectInvoker}} behind {{CachedMethod.invoke}} only.
> Internal types live in {{org.apache.groovy.internal.runtime.invoke}} 
> (japicmp-excluded). Definition reuses {{HiddenClassDefiner}} (GROOVY-12223) 
> and {{ClassLoaderForClassArtifacts}}.
> Define order:
> # InvokerFactory nestmate + direct invoke when the member is publicly 
> invocable from that class ({{String.startsWith}}).
> # Declaring-class nestmate + direct invoke when {{privateLookupIn}} is 
> possible. Private class methods use {{invokevirtual}}; private interface 
> methods use {{invokeinterface}} (hidden nestmates do not subclass the host, 
> so {{invokespecial}} fails verification).
> # InvokerFactory nestmate + classData {{MethodHandle}} + {{invokeExact}} when 
> types are still resolvable from the runtime loader.
> # {{ClassLoaderForClassArtifacts}} when the host loader can resolve 
> {{DirectInvoker}} — never for bootstrap types.
> Failures sticky-return {{null}}; {{CachedMethod.invoke}} keeps 
> {{Method.invoke}}. Generation is skipped for caller-sensitive and abstract 
> methods, Android, native image, and when hidden classes are disabled.
> This is the MOP "Groovy as caller" path ({{makeAccessible}}). Indy continues 
> to {{unreflect}} with the call-site {{Lookup}} and must not be fed the 
> trampoline.
> h2. Configuration
> {noformat}
> -Dgroovy.cachedmethod.invoker.threshold=100
> -Dgroovy.cachedmethod.invoker.disable=true
> {noformat}
> The existing {{-Dgroovy.hidden.classes.disable=true}} also turns generation 
> off.
> h2. Compatibility
> * No change to the {{MetaMethod.invoke}} / {{CachedMethod.invoke}} signatures.
> * Selection (categories, EMC, interceptable, per-instance MetaClass) is 
> unchanged; the trampoline is bound to the Java {{Method}}, not to a 
> {{MetaMethod}} wrapper.
> * Wrong-argument type on the generated path is {{ClassCastException}} 
> (rethrown), matching DGM / {{CallSiteGenerator}}. The reflective path still 
> wraps {{IllegalArgumentException}} in {{InvokerInvocationException}}.
> * Opt-out: {{-Dgroovy.cachedmethod.invoker.disable=true}}.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to