jamesfredley opened a new pull request, #2390:
URL: https://github.com/apache/groovy/pull/2390

   ## Summary
   
   Adds three new JMH benchmark classes in `org.apache.groovy.perf.grails` that 
reproduce the real-world invokedynamic performance regression observed in 
Grails 7 applications (GROOVY-10307).
   
   ## Benchmark Classes
   
   ### CallSiteInvalidationBench (11 benchmarks)
   
   Tests the core SwitchPoint invalidation mechanism - the root cause of the 
regression. Demonstrates that modifying ANY metaclass triggers global 
invalidation affecting ALL call sites:
   
   - **Cross-type invalidation** at 3 frequencies (every 100/1000/10000 
iterations)
   - **Same-type invalidation** for comparison
   - **Multiple call sites** scaling (5 concurrent call sites)
   - **Burst-then-steady-state** simulating framework startup
   
   Key result: `baselineHotLoop` (0.49ms) vs `crossTypeInvalidationEvery1000` 
(467ms) = **~950x overhead**
   
   ### MetaclassVariationBench (9 benchmarks)
   
   Tests GORM-like patterns where domain classes get per-instance 
ExpandoMetaClass enhancements:
   
   - **Shared vs per-instance metaclass** dispatch
   - **Multi-class startup simulation** (4 domain types enhanced in sequence)
   - **Dynamic finder** calls via static metaclass injection
   - **Per-instance EMC with ongoing churn** (worst case)
   
   Key result: `baselineSharedMetaclass` (2.0ms) vs `perInstanceMetaclass` 
(420ms) = **~207x overhead**
   
   ### GrailsWorkloadBench (14 benchmarks)
   
   Tests patterns extracted from the 
[grails7-performance-regression](https://github.com/jglapa/grails7-performance-regression)
 demo app's `PerformanceTestService`:
   
   - **Collection closure chains** 
(`.findAll{}.collect{}.groupBy{}.collectEntries{}`)
   - **Spread operator** (`employees*.firstName`)
   - **Nested closure delegation** (3-level criteria-like DSL)
   - **GString interpolation** with dynamic property access
   - **Dynamic property by name** (`this."$field"`)
   - **Project metrics aggregation** (`.count{}`, `.sum{}`, map building)
   - **Full analysis** combining all patterns
   
   Key result: `baselineCollectionClosureChain` (199ms) vs with invalidation 
(1631ms) = **~8.2x overhead** (matches demo app's observed 8.2x bootRun 
regression)
   
   ## Verification Results
   
   Each benchmark has a baseline (no metaclass changes) and an invalidation 
variant (periodic cross-type metaclass modifications). The delta quantifies the 
SwitchPoint invalidation overhead.
   
   Also tested against PR #2377's optimization (disabling SwitchPoint guards + 
explicit cache clearing):
   
   - **Baselines regressed 34-2851%** (steady-state dispatch worse without 
SwitchPoint guards)
   - **Invalidation benchmarks mostly regressed** (62-183% worse in most cases)
   - **Only 3 of 33 benchmarks improved** (10-28% faster in specific complex 
patterns)
   
   The benchmarks confirm that the SwitchPoint guard is critical for JIT 
optimization of stable call sites, and removing it (as PR #2377 does) trades 
steady-state performance for marginal churn resilience.
   
   ## Running
   
   ```bash
   # All new benchmarks
   ./gradlew :performance:jmh 
-PbenchInclude="CallSiteInvalidation|MetaclassVariation|GrailsWorkload"
   
   # Individual benchmark class
   ./gradlew :performance:jmh -PbenchInclude=CallSiteInvalidation
   ```
   
   ## Related
   
   - [GROOVY-10307](https://issues.apache.org/jira/browse/GROOVY-10307)
   - [grails-core #15293](https://github.com/apache/grails-core/issues/15293)
   - [Demo app](https://github.com/jglapa/grails7-performance-regression) 
showing 5-8x regression
   - PR #2377 (SwitchPoint guard optimization - benchmarked here)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to