[ 
https://issues.apache.org/jira/browse/GROOVY-12381?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King updated GROOVY-12381:
-------------------------------
    Attachment: aot-cache-measure.py

> Leyden: measure JDK 25 AOT cache on dynamic Groovy; indy sites relink per 
> start
> -------------------------------------------------------------------------------
>
>                 Key: GROOVY-12381
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12381
>             Project: Groovy
>          Issue Type: Task
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>         Attachments: aot-cache-measure.py
>
>
> Follow-up 2 from GROOVY-12365: a measurement pass over OpenJDK's Leyden work 
> (JEP 483 ahead-of-time class loading and linking, JEP 514 AOT cache 
> ergonomics, JEP 515 method profiling) as shipped in JDK 25, to see what it 
> gives a *dynamic* Groovy program and where Groovy's own dispatch stands in 
> the way. No code change; this issue records the numbers and the conclusions 
> so the discussion has a baseline.
> h3. Setup
> * The native-image probe from {{subprojects/tests-native}} (dynamic Groovy: 
> closures, traits, ranges, GStrings, metaclass access, async scope, NIO, XML 
> and JSON), compiled once and jarred. The AOT cache requires jar entries on 
> the class path; a populated class directory fails with "Cannot have non-empty 
> directory in paths".
> * Zulu 25.0.4 and Amazon Corretto 26.0.1, macOS aarch64. Training and cache 
> creation in one step: {{java -XX:AOTCacheOutput=probe.aot 
> -XX:+AOTClassLinking -cp ... probe}}; use: {{java -XX:AOTCache=probe.aot 
> -XX:+AOTClassLinking ...}}. Twelve interleaved runs per mode; class-load 
> sources from {{-Xlog:class+load}}; Groovy link events from 
> {{-Dgroovy.indy.logging=true}}; archived {{invokedynamic}} entries from 
> {{-Xlog:aot+resolve=trace}} at training.
> h3. Results (JDK 25; JDK 26 identical within noise)
> ||measure||no cache||AOT cache||
> |startup + run, median|409 ms|236 ms|
> |classes loaded from the cache|1256 (default CDS archive)|2973 of 3261|
> |classes loaded from jars|668|8|
> |hidden classes defined at run time|216|1|
> |LambdaForms spun at run time|293|262|
> |Groovy indy link events|125|125|
> * Cache size about 30 MB. Seven Groovy classes are skipped as "Unlinked class 
> not supported by AOTClassLinking": the compiler's ASM 
> {{ClassWriter}}/{{ClassRemapper}} family, which the probe never links.
> * The training log reports every one of the 275 {{invokedynamic}} 
> constant-pool entries archived. All of them are {{LambdaMetafactory}} or 
> {{StringConcatFactory}} sites: 103 in the JDK and 172 inside javac-compiled 
> Groovy runtime classes ({{NumberMathModificationInfo}} 78, {{Java9}} 14, 
> {{Closure}} 14, {{MetaClassImpl}} 6, {{MetaClassRegistryImpl}} 5). The probe 
> class has 94 {{IndyInterface}}-bootstrapped sites; none is archived. As 
> expected, JDK 25 archives its own bootstraps only.
> * The reflective cold tier ({{-Dgroovy.indy.cold.reflection=true}}, cache 
> trained with it on) changes neither the times nor the number of spun 
> LambdaForms.
> h3. Conclusions
> # For users the gain is real and free: about 40% off startup of a dynamic 
> program from class loading and linking alone, with no Groovy change. Worth a 
> paragraph in the documentation next to the native-image section, including 
> the jar-only class-path rule.
> # The residual cost is Groovy's call-site linking: the link count is 
> unchanged and roughly 260 LambdaForms are still spun on every start. Two 
> independent obstacles keep the cache from removing it:
> #* JDK 25 does not archive {{invokedynamic}} sites for non-JDK bootstrap 
> methods at all.
> #* Even if it did, {{IndyInterface.bootstrap}} returns a {{MutableCallSite}} 
> with a fallback target; method selection and guarded-chain construction 
> happen on first invocation and hold {{SwitchPoint}} and metaclass state, none 
> of which is archivable. An archived bootstrap result would still relink per 
> start.
> # A Groovy-side answer would need selection results that survive archiving: 
> the AOT link mode's idea of a permanently linked site, built during a 
> training run from handle graphs the cache can hold. That is a dispatch-design 
> question (GEP-31 territory), not a fix for 6.0.
> h3. Next steps
> * Repeat the same script on the Leyden early-access build (premain branch, 
> jdk.java.net/leyden), which adds AOT-compiled code and has been widening what 
> {{invokedynamic}} sites are archived; the question is whether the residual 
> 262 LambdaForms and 125 links shrink there.
> * Document the JDK 25 recipe and numbers for users.
> * Keep the measurement script with the native probe so it can be rerun as 
> JDKs move; the probe already exists for exactly this purpose.



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

Reply via email to