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]
