Daniel Sun created GROOVY-12191:
-----------------------------------

             Summary: Scope indy SwitchPoint invalidation and harden PIC/math 
paths
                 Key: GROOVY-12191
                 URL: https://issues.apache.org/jira/browse/GROOVY-12191
             Project: Groovy
          Issue Type: Improvement
            Reporter: Daniel Sun


h3. Problem this targets

With invokedynamic enabled (default since Groovy 4), a *single process-wide* 
{{SwitchPoint}} guarded essentially all linked MOP call sites. Any MetaClass 
registry change or category enter/leave invalidated that switch point, so *all* 
sites fell back and re-linked — including sites whose receiver type was 
unrelated.

That global invalidation shows up as:
 * Startup / request paths with ExpandoMetaClass / mixins (Grails-like 
patterns) pay repeated re-link cost
 * Category {{use}} thrashing deoptimizes the whole application
 * JMH benches under {{subprojects/performance}} document this cost pattern

h3. What changes proposed

Four complementary layers under the indy / MOP runtime:
||#||Area||Behaviour||
|1|Scoped SwitchPoint invalidation|Per-{{{}ClassInfo{}}} domain + hierarchy 
fan-out to loaded subtypes; separate *category-global* domain for 
{{{}use(Category){}}}. MetaClass change for class {{A}} no longer invalidates 
sites for unrelated class {{B}}|
|2|PIC polymorphism policy|{{CallSiteTracker}} counts distinct receiver shapes; 
at {{max.poly}} the site becomes sticky *megamorphic* (pin default boot target, 
no monomorphic {{setTarget}} thrash). Selection stays amortized via the PIC|
|3|Bimorphic promote|Two stable receiver shapes can share one nested 
{{guardWithTest}} target instead of alternating monomorphic targets|
|4|IndyMath completeness|BigDecimal-category ops (and mixed integral+BD) via 
{{{}NumberMath{}}}; {{div}} / {{{}intdiv{}}}; primitive {{rightShiftUnsigned}} 
table entries. Shift ops stay off the GeneratedMetaMethod→IndyMath fast path so 
mixed {{{}int{}}}/{{{}long{}}} keeps left-operand result typing|
h3. Invalidation model (after the changes)
||Domain||Invalidated when||Guards||
|Per-class (+ subtypes)|{{ClassInfo.incVersion}} / MetaClass registry change 
for that type|Receiver class (or {{Class}} for static sites); hierarchy fan-out 
invalidates subtype SwitchPoints|
|Category-global|Category enter/leave 
({{{}VMPlugin.invalidateCallSites{}}})|Always dual-guarded with the class 
domain; *never* fails over (correctness)|

Per-class SwitchPoint can fail over after repeated retirements (default 100) to 
a {{ClassInfo.version}} equality guard (avoids permanent-invalid SP recursion). 
Concurrent failover drains any raced-in SwitchPoint so none remains valid after 
the domain has failed over. Category domain stays unlimited.

Generation bump on MetaClass *replacement* / clear retires that class's 
SwitchPoint; first MetaClass install only bumps version (no linked sites yet). 
Hierarchy fan-out remains on {{incVersion}} and registry listeners.
h3. Tunables (system properties)
||Property||Default||Meaning||
|{{groovy.indy.switchpoint.max.failures}}|{{100}}|Live SP retirements before 
per-class failover (value less than or equal to 0 = unlimited)|
|{{groovy.indy.invalidation.stats}}|{{false}}|Log invalidation counters|
|{{groovy.indy.callsite.max.poly}}|{{8}}|Distinct shapes to sticky megamorphic 
(value less than or equal to 0 disables)|
|{{groovy.indy.callsite.mega.sticky}}|{{true}}|Mega flag survives shape clear|
|{{groovy.indy.callsite.bimorphic}}|{{true}}|Allow dual-shape target promote|
|{{groovy.indy.callsite.cache.size}}|{{8}}|PIC LRU size|



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

Reply via email to