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

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

daniellansun commented on PR #2736:
URL: https://github.com/apache/groovy/pull/2736#issuecomment-5091630102

   > There is one point that escapes me right now. Why do we need the hierarchy 
at all? I don´t think this is for MetaClassImpl cases.
   
   You are right that this is **not** because `MetaClassImpl` shares one method 
table up the hierarchy. Each class still has its own MetaClass / `ClassInfo` 
domain.
   
   Hierarchy fan-out exists for **cross-class MOP visibility** that 
already-linked subtype sites must re-observe:
   
   1. **Parent ExpandoMetaClass / registry mutation**  
      `Parent.metaClass.foo = { … }` must force sites linked for `Child` to 
re-select so `new Child().foo()` sees the method. Without retiring Child’s 
SwitchPoint, a previously linked miss (or older target) stays installed.
   
   2. **Interface MetaClass changes**  
      Same rule for implementors.
   
   3. **Array covariance**  
      `Object[]` / interface arrays are MOP-relevant supertypes of reference 
arrays even though array classes are `final` — a pure “exact class only” domain 
would leave `String[]` sites stale after `Object[]` MetaClass changes.
   
   So: fan-out is about *inherited / covariant MOP state*, not about rewriting 
`MetaClassImpl` internals. Category enter/leave remains a separate bulk path 
and does not rely on this index for semantics.
   
   **Action taken**
   
   - Documented this rationale on `IndyInvalidation`, `ClassHierarchyIndex`, 
and `package-info`.
   - Kept / extended tests:
     - parent EMC → child SwitchPoint + runtime visibility
     - parent `MetaClassImpl` *replacement* still fans out (registry change can 
still affect subtype dispatch)
     - unrelated-type churn does **not** touch an unrelated hierarchy
     - array / interface-array lattice cases (from earlier review)
   
   Happy to discuss a narrower fan-out (e.g. EMC-only) as a follow-up 
optimization; the current rule prefers correctness and matches pre-6.0 
“something in the MOP above me changed → re-link” behaviour, but scoped to the 
subtype cone instead of the whole process.




> Scope indy SwitchPoint invalidation
> -----------------------------------
>
>                 Key: GROOVY-12191
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12191
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Daniel Sun
>            Priority: Major
>             Fix For: 6.0.0-beta-1
>
>
> h3. Problem
> With invokedynamic enabled (default since Groovy 4), linked MOP call sites 
> were guarded by a *single process-wide* {{SwitchPoint}} 
> ({{{}IndyInterface.switchPoint{}}}).
> Any MetaClass registry change or category enter/leave invalidated that switch 
> point, so *every* linked site fell back and re-linked — including sites whose 
> receiver type was unrelated.
> That global invalidation is expensive when MetaClass churn is common (e.g. 
> ExpandoMetaClass / mixins on startup or per-request paths, Grails-like 
> patterns). Unrelated hot monomorphic sites pay re-link and JIT deopt cost 
> they should not.
> h3. Goal
> Keep linked call sites optimized unless the *relevant* MetaClass state for 
> that site actually changed.
> h3. Approach
> One SwitchPoint domain {*}per class{*}, stored on {{{}ClassInfo{}}}:
>  * MetaClass change for type {{T}} retires {{{}T{}}}'s SwitchPoint *and* 
> those of loaded subtypes / implementors (hierarchy fan-out).
>  * Unrelated types keep their SwitchPoints; their call sites stay optimized.
>  * Category enter/leave (and {{{}VMPlugin.invalidateCallSites(){}}}) 
> bulk-retire *all* loaded class SwitchPoints so sites re-link under the new 
> category state. There is *no* second category SwitchPoint on the hot path.
>  * Linked handles always install a *single* class-domain guard via 
> {{IndyInvalidation.guardWithMopSwitchPoints(...)}} — same monomorphic guard 
> shape as before, without global deopt on unrelated MetaClass churn.
> Final classes short-circuit hierarchy fan-out (no full {{ClassInfo}} scan). 
> Non-final types batch retirements with {{{}SwitchPoint.invalidateAll{}}}.
> h3. Invalidation map
> ||Event||What is retired||
> |MetaClass change for type {{T}} (registry / 
> {{{}ClassInfo.incVersion{}}})|{{T}} + loaded subtypes / implementors|
> |Category enter/leave, {{invalidateCallSites()}}|All loaded class 
> SwitchPoints (bulk)|
> |Unattributed MetaClass registry event|All loaded class SwitchPoints (bulk)|
> |First MetaClass *install* on a class|Version bump only (no linked sites 
> yet); replacement / clear retires that class's SwitchPoint|
>  



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

Reply via email to