Re: version planning
Paul King <[email protected]> Tue, 7 Jul 2026 22:54:16 +1000
| Newsgroups | gmane.comp.lang.groovy.devel |
|---|---|
| Message-ID | <CAMbkE7QggLPEvSPRr0RexGDZbiwrfxJUCV_SkgiUvRjCjqLeVA@mail.gmail.com> |
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 f= ree) guardWithTest x8 / catchException x8 +5 / +6 (same pattern) dropArguments, arities 1-8 +26 (~3.2 per resulting sh= ape) asCollector / asSpreader, arities 1-8 +34 / +24 (~3-4 per arity) 8 raw handles, distinct primitive signatures +36 (~4.5 per basic-type s= hape) 8 raw handles, distinct reference signatures +8 (all erase to L) So yes =E2=80=94 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 =E2=80=94 and if we do build it, hanging th= e 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=E2=80=AFPM 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 (integ= er > > throughput, pointer-chase, allocation churn) now run inside every CI su= ite > > shard, and the per-PR summary comment shows a hardware-calibrated speed= up > > column next to the raw one, plus a warning when the runner deviates >15= % from > > the baseline hardware. Your caveat =E2=80=94 that a runner isn't unifor= m *within* a > > run either =E2=80=94 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/ar= ity) > > primitive arg diversity (8 primitive shapes) +110 (~13 LF/sh= ape) > > one (R,int)Object call site (control) +29 > > primitive RETURN diversity (vs uniform int +14) +27 (~2 LF ea= ch) > > 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 = =E2=80=94 the > > JVM already shares LambdaForms across reference-typed shapes, so er= asure > > 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 dimensio= ns 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 =E2=80=94 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 (th= e > > reflection/MOP-dispatching cold path, your hidden-class template id= ea): an > > (Object, Object[])Object cold dispatcher erases both expensive dime= nsions > > at once =E2=80=94 one shape for every arity and every primitive pat= tern =E2=80=94 which is > > precisely why reflection is cold-fast. The specialised primitive sh= apes > > 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 >