Hi Jochen, Response below.
Cheers, Paul. -------- > This means asType adaption and explicitCastArguments does not cost? Also > interesting would be the cost of inserting and dropping arguments in > terms of Lambda forms Measured (pure java.lang.invoke, no Groovy, fresh JVM per mode, count of spun LambdaForm$* classes; 8 variations per mode, minus a raw-handle control where applicable): asType / explicitCastArguments (ref or prim) +0 over the raw handles insertArguments x8 (same shape) +7 total (one-time, then free) guardWithTest x8 / catchException x8 +5 / +6 (same pattern) dropArguments, arities 1-8 +26 (~3.2 per resulting shape) asCollector / asSpreader, arities 1-8 +34 / +24 (~3-4 per arity) 8 raw handles, distinct primitive signatures +36 (~4.5 per basic-type shape) 8 raw handles, distinct reference signatures +8 (all erase to L) So yes — asType/explicitCastArguments literally cost nothing by themselves. The law that explains all of it (and the earlier table you weren't sure how to read): LambdaForm cost tracks DISTINCT BASIC-TYPE SHAPES touched, not combinators and not call sites. Reference types all erase to basic type L, so reference diversity is free; primitives and arity are the basic-type dimensions, so they are the only real multipliers. Your mismatch theory was right in effect but the mechanism is shape-counting: the adaptation step is innocent, the primitive shapes it connects are the cost. In our full chains one site-shape drags ~13-17 LFs because bootstrap collector + guards + spreader each derive shapes from it. Earlier-table reading guide: each row is one generated workload varying one dimension, run in a fresh JVM; the number is loaded LambdaForm classes minus the 142 a bare Groovy runtime loads. "32 sites vs 1 site: +29 vs +29" means sites are free at constant shape. > still possible that creating those hidden classes costs too much though. > Also we need an efficient lookup for those [...] The GROOVY-12137 spike suggests the template class may be optional rather than required: plain Method.invoke (which shares the JDK's own per-arity infrastructure post-JEP-416) already removes ~2/3 of the per-shape LF cost and gives 1.34-1.40x faster cold dispatch on many-cold-sites workloads, with zero per-method class creation and no lookup structure at all. Promotion bounds reflective residence to at most threshold calls per receiver shape, after which the hot path is byte-for-byte what we have today. So the hidden-class template becomes a targeted optimisation of cold per-call overhead, to be justified by measurement, not a prerequisite — and if we do build it, hanging the cache off CachedMethod (like the old callsite-constructor cache, so classloader lifecycle comes for free) seems the natural lookup. On Tue, Jul 7, 2026 at 9:48 PM Jochen Theodorou <[email protected]> wrote: > > On 7/7/26 11:48, Paul King wrote: > [...] > >> My idea would be to have a baseline benchmark in Groovy unrelated code > >> in pure Java. And then adjust everything to be relative to that. > > > > Your mail crossed the implementation :-) That's exactly what landed as > > GROOVY-12127 on July 5: pure-Java "calibration ruler" benchmarks (integer > > throughput, pointer-chase, allocation churn) now run inside every CI suite > > shard, and the per-PR summary comment shows a hardware-calibrated speedup > > column next to the raw one, plus a warning when the runner deviates >15% > > from > > the baseline hardware. Your caveat — that a runner isn't uniform *within* a > > run either — is fair; the planned refinement is a second ruler that sorts to > > the end of each suite's execution so we can report intra-run drift too. > > nice > > [...]> Small generated workloads, each varying ONE dimension, delta > LambdaForms vs a > > 142-LF baseline JVM (deterministic, reproducible): > > > > arity diversity (arities 1-8, all-Object args) +136 (~17 LF/arity) > > primitive arg diversity (8 primitive shapes) +110 (~13 LF/shape) > > one (R,int)Object call site (control) +29 > > primitive RETURN diversity (vs uniform int +14) +27 (~2 LF each) > > 8 sites, all-Object args (control) +17 > > reference arg diversity (8 distinct classes) +16 (~free) > > reference return diversity +12 (~free) > > 32 sites vs 1 site, identical shape+target +29 vs +29 (0 per > > site) > > interesting, though not sure I interpret the numbers correctly > > > Conclusions: > > > > 1. My W2 proposal (erase reference types in indy descriptors) is dead — the > > JVM already shares LambdaForms across reference-typed shapes, so erasure > > would buy roughly nothing. Your skepticism was correct; no compiler > > change > > should be written for that. > > > > 2. Your mismatch theory is confirmed, with a refinement: the > > callsite<->target > > adaptation cost is real but specifically for the PRIMITIVE dimensions of > > MethodType (boxing adaptations and per-arity forms). Reference-type > > mismatches fold into shared forms for free. LambdaForms are shared > > across > > call sites of identical shape — the cost is per SHAPE, not per site. > > per shape was clear to me. The Reference-type mismatch being essentially > free but not primitives is kind of surprising. I mean it is clear to me > that the primitives do cost, but reference types being free not so much. > This means asType adaption and explicitCastArguments does not cost? Also > interesting would be the cost of inserting and dropping arguments in > terms of Lambda forms > > > 3. This strengthens the cold-tier direction you're already circling (the > > reflection/MOP-dispatching cold path, your hidden-class template idea): > > an > > (Object, Object[])Object cold dispatcher erases both expensive > > dimensions > > at once — one shape for every arity and every primitive pattern — which > > is > > precisely why reflection is cold-fast. The specialised primitive shapes > > then get paid lazily, only by the sites that earn promotion to hot. > > still possible that creating those hidden classes costs too much though. > Also we need an efficient lookup for those, since we would have one per > "called" method in the receiver globally... well per module and > classloader would also work for JPMS compatibility. > > bye Jochen >
