Skip to content

GROOVY-10307: add JMH benchmarks for invokedynamic performance patterns#2381

Merged
paulk-asert merged 2 commits intoapache:masterfrom
jamesfredley:GROOVY-10307-jmh-benchmarks
Feb 16, 2026
Merged

GROOVY-10307: add JMH benchmarks for invokedynamic performance patterns#2381
paulk-asert merged 2 commits intoapache:masterfrom
jamesfredley:GROOVY-10307-jmh-benchmarks

Conversation

@jamesfredley
Copy link
Contributor

https://issues.apache.org/jira/browse/GROOVY-10307

Adds 7 JMH benchmark files to the performance subproject covering common Groovy patterns exercised through invokedynamic. These benchmarks complement the existing org.apache.groovy.bench suite (Ackermann, Ary, Fibo, GeneratedHashCode, Callsite) by focusing on Groovy-specific language features.

Run with: ./gradlew perf:jmh -PbenchInclude=perf

Benchmark Files

File Benchmarks Coverage
ClosureBench 19 Creation, reuse, multi-param, capture, delegation, nesting, method-ref, curry/rcurry, composition, spread, trampoline, each/collect/findAll/inject
LoopsBench 5 Closure-in-loop vs method-in-loop, nested iteration, call site caching
MethodInvocationBench 9 Instance, static, parameterized, overloaded, monomorphic/polymorphic call sites, interface dispatch, dynamic-typed
GStringBench 5 Simple/multi-value interpolation, string-concat baseline, map-key usage, repeated toString
PropertyAccessBench 6 Field read/write, getter/setter, dynamic-typed, map bracket/dot-property, chained access
OperatorBench 13 Arithmetic, BigDecimal, string multiply, list/map getAt/putAt, left-shift, equals, spaceship, comparison, unary minus, in-operator
GroovyIdiomBench 17 Safe navigation (?.), spread-dot (*.), elvis (?:), with/tap, range creation/iteration/contains, as-coercion

74 benchmarks total, all using proper Blackhole consumption to prevent dead-code elimination.

Copilot AI review requested due to automatic review settings February 15, 2026 23:35
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new org.apache.groovy.perf JMH benchmark suite under the performance subproject to measure invokedynamic-heavy Groovy language patterns (closures, operators, property access, GStrings, etc.), complementing the existing org.apache.groovy.bench benchmarks.

Changes:

  • Add 7 new Groovy JMH benchmark classes under subprojects/performance/src/jmh/groovy/org/apache/groovy/perf.
  • Cover Groovy-specific invokedynamic patterns: closure usage, loop/dispatch patterns, method invocation shapes, GString usage, property access styles, operator overloading, and common Groovy idioms.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/ClosureBench.groovy Adds closure-focused microbenchmarks (creation, reuse, delegation, curry, trampoline, collection ops).
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/GStringBench.groovy Adds GString interpolation/toString/map-key usage benchmarks.
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/GroovyIdiomBench.groovy Adds benchmarks for Groovy idioms (safe-nav, spread-dot, elvis, with/tap, ranges, as coercion).
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/LoopsBench.groovy Adds loop-centric benchmarks comparing closure-in-loop vs method calls and nested iteration patterns.
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/MethodInvocationBench.groovy Adds benchmarks for method call shapes (instance/static, overload resolution, mono/poly call sites, interface dispatch, dynamic receiver).
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/OperatorBench.groovy Adds operator-overloading benchmarks (arithmetic, BigDecimal, list/map subscripts, leftShift, equals, spaceship, comparisons, unary minus, in).
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/PropertyAccessBench.groovy Adds benchmarks for property/field/getter-setter access, dynamic receiver property access, map bracket/dot property, and chaining.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jamesfredley jamesfredley force-pushed the GROOVY-10307-jmh-benchmarks branch from 8ec3637 to b86daf9 Compare February 15, 2026 23:50
@codecov-commenter
Copy link

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.7163%. Comparing base (9b4ec31) to head (b86daf9).

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #2381        +/-   ##
==================================================
+ Coverage     66.7154%   66.7163%   +0.0009%     
- Complexity      29846      29847         +1     
==================================================
  Files            1382       1382                
  Lines          116180     116180                
  Branches        20491      20491                
==================================================
+ Hits            77510      77511         +1     
  Misses          32323      32323                
+ Partials         6347       6346         -1     

see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@paulk-asert
Copy link
Contributor

LGTM

@paulk-asert paulk-asert merged commit 61dae3d into apache:master Feb 16, 2026
15 checks passed
@jamesfredley jamesfredley deleted the GROOVY-10307-jmh-benchmarks branch February 16, 2026 00:20
jamesfredley added a commit to jamesfredley/groovy that referenced this pull request Feb 26, 2026
…oints

Add 43 JMH benchmarks across 4 files targeting the metaclass invalidation
patterns that cause performance regression in Grails applications under
invokedynamic. These complement the general-purpose benchmarks from apache#2381
by exercising the specific dynamic dispatch patterns identified in apache#2374
and apache#2377.

New benchmark files:

MetaclassChangeBench (11 benchmarks)
- ExpandoMetaClass method addition during method dispatch
- Metaclass replacement cycles
- Multi-class invalidation cascade (change on ServiceA invalidates ServiceB/C)
- Burst-then-steady-state (simulates Grails startup then request handling)
- Property access and closure dispatch under metaclass churn

CategoryBench (10 benchmarks)
- use(Category) blocks inside vs outside loops
- Nested and simultaneous multi-category scopes
- Collateral invalidation damage on non-category call sites
- Category method shadowing existing methods

DynamicDispatchBench (12 benchmarks)
- methodMissing with single/rotating names (dynamic finders)
- propertyMissing read/write (Grails params/session)
- GroovyInterceptable invokeMethod interception (transactional services)
- ExpandoMetaClass-injected method calls mixed with real methods
- def-typed monomorphic and polymorphic dispatch

GrailsLikePatternsBench (10 benchmarks)
- Service chain: validation, CRUD, collection processing
- Controller action: param binding, service call, model/view rendering
- Domain validation with dynamic property access (this."$field")
- Configuration DSL with nested @DelegatesTo closures
- Markup builder with nested tag/closure rendering
- Full request cycle simulation with and without metaclass churn

Run with: ./gradlew perf:jmh -PbenchInclude=perf
jamesfredley added a commit to jamesfredley/groovy that referenced this pull request Feb 26, 2026
…oints

Add 43 JMH benchmarks across 4 files targeting the metaclass invalidation
patterns that cause performance regression in Grails applications under
invokedynamic. These complement the general-purpose benchmarks from apache#2381
by exercising the specific dynamic dispatch patterns identified in apache#2374
and apache#2377.

New benchmark files:

MetaclassChangeBench (11 benchmarks)
- ExpandoMetaClass method addition during method dispatch
- Metaclass replacement cycles
- Multi-class invalidation cascade (change on ServiceA invalidates ServiceB/C)
- Burst-then-steady-state (simulates Grails startup then request handling)
- Property access and closure dispatch under metaclass churn

CategoryBench (10 benchmarks)
- use(Category) blocks inside vs outside loops
- Nested and simultaneous multi-category scopes
- Collateral invalidation damage on non-category call sites
- Category method shadowing existing methods

DynamicDispatchBench (12 benchmarks)
- methodMissing with single/rotating names (dynamic finders)
- propertyMissing read/write (Grails params/session)
- GroovyInterceptable invokeMethod interception (transactional services)
- ExpandoMetaClass-injected method calls mixed with real methods
- def-typed monomorphic and polymorphic dispatch

GrailsLikePatternsBench (10 benchmarks)
- Service chain: validation, CRUD, collection processing
- Controller action: param binding, service call, model/view rendering
- Domain validation with dynamic property access (this."$field")
- Configuration DSL with nested @DelegatesTo closures
- Markup builder with nested tag/closure rendering
- Full request cycle simulation with and without metaclass churn

Run with: ./gradlew perf:jmh -PbenchInclude=perf
paulk-asert pushed a commit that referenced this pull request Feb 27, 2026
…oints

Add 43 JMH benchmarks across 4 files targeting the metaclass invalidation
patterns that cause performance regression in Grails applications under
invokedynamic. These complement the general-purpose benchmarks from #2381
by exercising the specific dynamic dispatch patterns identified in #2374
and #2377.

New benchmark files:

MetaclassChangeBench (11 benchmarks)
- ExpandoMetaClass method addition during method dispatch
- Metaclass replacement cycles
- Multi-class invalidation cascade (change on ServiceA invalidates ServiceB/C)
- Burst-then-steady-state (simulates Grails startup then request handling)
- Property access and closure dispatch under metaclass churn

CategoryBench (10 benchmarks)
- use(Category) blocks inside vs outside loops
- Nested and simultaneous multi-category scopes
- Collateral invalidation damage on non-category call sites
- Category method shadowing existing methods

DynamicDispatchBench (12 benchmarks)
- methodMissing with single/rotating names (dynamic finders)
- propertyMissing read/write (Grails params/session)
- GroovyInterceptable invokeMethod interception (transactional services)
- ExpandoMetaClass-injected method calls mixed with real methods
- def-typed monomorphic and polymorphic dispatch

GrailsLikePatternsBench (10 benchmarks)
- Service chain: validation, CRUD, collection processing
- Controller action: param binding, service call, model/view rendering
- Domain validation with dynamic property access (this."$field")
- Configuration DSL with nested @DelegatesTo closures
- Markup builder with nested tag/closure rendering
- Full request cycle simulation with and without metaclass churn

Run with: ./gradlew perf:jmh -PbenchInclude=perf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants