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

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

jamesfredley commented on code in PR #2390:
URL: https://github.com/apache/groovy/pull/2390#discussion_r2878203933


##########
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/grails/MetaclassVariationBench.groovy:
##########
@@ -0,0 +1,259 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.perf.grails
+
+import groovy.lang.ExpandoMetaClass
+import groovy.lang.GroovySystem
+
+import org.openjdk.jmh.annotations.*
+import org.openjdk.jmh.infra.Blackhole
+
+import java.util.concurrent.TimeUnit
+
+/**
+ * Per-instance metaclass variation overhead (GORM domain class enhancement 
pattern).
+ *
+ * @see <a 
href="https://issues.apache.org/jira/browse/GROOVY-10307";>GROOVY-10307</a>
+ */
+@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
+@Fork(2)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Thread)
+class MetaclassVariationBench {
+    static final int ITERATIONS = 100_000
+    static final int INSTANCE_COUNT = 20
+
+    // Simulates a GORM domain class
+    static class DomainEntity {
+        Long id
+        String name
+        String email
+        boolean active = true
+        int version = 0
+
+        String getFullName() { name ?: 'Unknown' }
+        boolean isActive() { active }
+        int getVersion() { version }
+
+        DomainEntity save() {
+            version++
+            if (id == null) id = System.nanoTime()
+            this
+        }
+
+        Map toMap() {
+            [id: id, name: name, email: email, active: active, version: 
version]
+        }
+    }
+
+    // Additional domain class types
+    static class DomainTypeB {
+        String label = "dept"
+        int count = 5
+        int getCount() { count }
+    }
+
+    static class DomainTypeC {
+        String status = "ACTIVE"
+        BigDecimal budget = 100000.0
+        String getStatus() { status }
+    }
+
+    static class DomainTypeD {
+        int priority = 5
+        String assignee = "unassigned"
+        int getPriority() { priority }
+    }
+
+    // Unrelated type for cross-type invalidation
+    static class ServiceType {
+        String config = "default"
+    }
+
+    List<DomainEntity> sharedMetaclassInstances
+    List<DomainEntity> perInstanceMetaclassInstances
+    DomainTypeB typeB
+    DomainTypeC typeC
+    DomainTypeD typeD
+
+    @Setup(Level.Iteration)
+    void setup() {
+        GroovySystem.metaClassRegistry.removeMetaClass(DomainEntity)
+        GroovySystem.metaClassRegistry.removeMetaClass(DomainTypeB)
+        GroovySystem.metaClassRegistry.removeMetaClass(DomainTypeC)
+        GroovySystem.metaClassRegistry.removeMetaClass(DomainTypeD)
+        GroovySystem.metaClassRegistry.removeMetaClass(ServiceType)
+
+        // Shared default class metaclass
+        sharedMetaclassInstances = (1..INSTANCE_COUNT).collect { i ->
+            new DomainEntity(id: i, name: "User$i", email: "user${i}@test.com")
+        }
+
+        // Per-instance ExpandoMetaClass (GORM trait pattern)
+        perInstanceMetaclassInstances = (1..INSTANCE_COUNT).collect { i ->
+            def entity = new DomainEntity(id: i, name: "Enhanced$i", email: 
"e${i}@test.com")
+            def emc = new ExpandoMetaClass(DomainEntity, false, true)
+            // GORM-injected methods
+            emc.validate = { -> delegate.name != null && delegate.email != 
null }
+            emc.delete = { -> delegate.id = null; delegate }
+            emc.addToDependencies = { item -> delegate }
+            emc.initialize()
+            entity.metaClass = emc
+            entity
+        }
+
+        typeB = new DomainTypeB()
+        typeC = new DomainTypeC()
+        typeD = new DomainTypeD()
+    }
+
+    /** Method calls on instances sharing default class metaclass. */
+    @Benchmark
+    void baselineSharedMetaclass(Blackhole bh) {
+        int sum = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def entity = sharedMetaclassInstances[i % INSTANCE_COUNT]
+            sum += entity.getFullName().length()
+            sum += entity.getVersion()
+        }
+        bh.consume(sum)
+    }
+
+    /** Method calls on instances each with their own ExpandoMetaClass. */
+    @Benchmark
+    void perInstanceMetaclass(Blackhole bh) {
+        int sum = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def entity = perInstanceMetaclassInstances[i % INSTANCE_COUNT]
+            sum += entity.getFullName().length()
+            sum += entity.getVersion()
+        }
+        bh.consume(sum)
+    }
+
+    /** Calling GORM-injected methods on per-instance EMC objects. */
+    @Benchmark
+    void perInstanceInjectedMethodCalls(Blackhole bh) {
+        int sum = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def entity = perInstanceMetaclassInstances[i % INSTANCE_COUNT]
+            boolean valid = entity.validate()
+            sum += valid ? 1 : 0
+        }
+        bh.consume(sum)
+    }
+
+    /** GORM startup: enhance 4 domain types then steady-state calls. */
+    @Benchmark
+    void multiClassStartupThenSteadyState(Blackhole bh) {
+        // Phase 1: Enhance 4 domain class types
+        DomainEntity.metaClass.static.findAllByName = { String n -> [] }
+        DomainEntity.metaClass.static.countByActive = { boolean a -> 0 }
+
+        DomainTypeB.metaClass.static.findAllByLabel = { String l -> [] }
+        DomainTypeB.metaClass.static.countByCount = { int c -> 0 }
+
+        DomainTypeC.metaClass.static.findAllByStatus = { String s -> [] }
+        DomainTypeC.metaClass.static.findByBudgetGreaterThan = { BigDecimal b 
-> null }
+
+        DomainTypeD.metaClass.static.findAllByPriority = { int p -> [] }
+        DomainTypeD.metaClass.static.findByAssignee = { String a -> null }
+
+        // Phase 2: Steady-state calls
+        int sum = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def entity = sharedMetaclassInstances[i % INSTANCE_COUNT]
+            sum += entity.getFullName().length()
+            sum += typeB.getCount()
+            sum += typeC.getStatus().length()
+            sum += typeD.getPriority()
+        }
+        bh.consume(sum)
+    }
+
+    /** Baseline: same steady-state work without preceding metaclass 
enhancements. */
+    @Benchmark
+    void baselineMultiClassNoStartup(Blackhole bh) {
+        int sum = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def entity = sharedMetaclassInstances[i % INSTANCE_COUNT]
+            sum += entity.getFullName().length()
+            sum += typeB.getCount()
+            sum += typeC.getStatus().length()
+            sum += typeD.getPriority()
+        }
+        bh.consume(sum)
+    }
+
+    /** Calling dynamic finders injected via static metaclass. */
+    @Benchmark
+    void dynamicFinderCalls(Blackhole bh) {
+        // Inject dynamic finders
+        DomainEntity.metaClass.static.findByName = { String n ->
+            [new DomainEntity(name: n)]
+        }
+        DomainEntity.metaClass.static.findAllByActive = { boolean a ->
+            [new DomainEntity(active: a)]
+        }
+
+        for (int i = 0; i < ITERATIONS / 10; i++) {
+            def result1 = DomainEntity.findByName("User${i % 10}")
+            def result2 = DomainEntity.findAllByActive(true)
+            bh.consume(result1)
+            bh.consume(result2)
+        }
+    }
+
+    /** Mixed compiled method calls and dynamic finder calls. */
+    @Benchmark
+    void mixedCompiledAndDynamicFinders(Blackhole bh) {
+        DomainEntity.metaClass.static.findByName = { String n ->
+            [new DomainEntity(name: n)]
+        }

Review Comment:
   Same as above - the injection is intentional to model per-request GORM cost. 
Updated Javadoc to clarify in 1af1c45.



##########
subprojects/performance/src/jmh/groovy/org/apache/groovy/perf/grails/GrailsWorkloadBench.groovy:
##########
@@ -0,0 +1,467 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.groovy.perf.grails
+
+import groovy.lang.GroovySystem
+
+import org.openjdk.jmh.annotations.*
+import org.openjdk.jmh.infra.Blackhole
+
+import java.util.concurrent.TimeUnit
+
+/**
+ * Groovy collection and DSL patterns from the grails7-performance-regression 
demo app.
+ *
+ * @see <a 
href="https://github.com/jglapa/grails7-performance-regression";>Demo app</a>
+ * @see <a 
href="https://issues.apache.org/jira/browse/GROOVY-10307";>GROOVY-10307</a>
+ */
+@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
+@Fork(2)
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Thread)
+class GrailsWorkloadBench {
+    static final int ITERATIONS = 10_000
+
+    // Domain-like entities from the demo app
+    static class Employee {
+        Long id
+        String firstName
+        String lastName
+        String email
+        String jobTitle
+        String department
+        BigDecimal salary
+        boolean isActive
+        int performanceRating
+        List<String> skills = []
+
+        String getFullName() { "$firstName $lastName" }
+        Map toMap() {
+            [id: id, name: getFullName(), email: email, title: jobTitle,
+             dept: department, salary: salary, active: isActive,
+             rating: performanceRating, skillCount: skills.size()]
+        }
+    }
+
+    static class Project {
+        Long id
+        String name
+        String status
+        BigDecimal budget
+        String department
+        int priority
+        List<Task> tasks = []
+        List<Milestone> milestones = []
+
+        Map toMap() {
+            [id: id, name: name, status: status, budget: budget,
+             taskCount: tasks.size(), milestoneCount: milestones.size()]
+        }
+    }
+
+    static class Task {
+        Long id
+        String name
+        String status
+        int priority
+        int estimatedHours
+        String assignee
+
+        Map toMap() { [id: id, name: name, status: status, priority: priority] 
}
+    }
+
+    static class Milestone {
+        Long id
+        String name
+        boolean isCompleted
+        Map toMap() { [id: id, name: name, completed: isCompleted] }
+    }
+
+    // Unrelated type for cross-type invalidation
+    static class PluginConfig {
+        String setting = "default"
+    }
+
+    List<Employee> employees
+    List<Project> projects
+    List<Task> tasks
+
+    @Setup(Level.Iteration)
+    void setup() {
+        GroovySystem.metaClassRegistry.removeMetaClass(Employee)
+        GroovySystem.metaClassRegistry.removeMetaClass(Project)
+        GroovySystem.metaClassRegistry.removeMetaClass(Task)
+        GroovySystem.metaClassRegistry.removeMetaClass(Milestone)
+        GroovySystem.metaClassRegistry.removeMetaClass(PluginConfig)
+
+        def statuses = ['TODO', 'IN_PROGRESS', 'DONE', 'BLOCKED']
+        def departments = ['Engineering', 'Marketing', 'Sales', 'Support', 
'HR']
+        def titles = ['Developer', 'Designer', 'Manager', 'Analyst', 'Lead']
+
+        // Sample data matching demo app scale
+        employees = (1..50).collect { i ->
+            new Employee(
+                id: i,
+                firstName: "First$i",
+                lastName: "Last$i",
+                email: "user${i}@example.com",
+                jobTitle: titles[i % titles.size()],
+                department: departments[i % departments.size()],
+                salary: 50000 + (i * 1000),
+                isActive: i % 5 != 0,
+                performanceRating: (i % 5) + 1,
+                skills: (1..(i % 4 + 1)).collect { s -> "Skill$s" }
+            )
+        }
+
+        tasks = (1..100).collect { i ->
+            new Task(
+                id: i,
+                name: "Task$i",
+                status: statuses[i % statuses.size()],
+                priority: (i % 10) + 1,
+                estimatedHours: (i % 8) + 1,
+                assignee: "First${(i % 50) + 1}"
+            )
+        }
+
+        projects = (1..20).collect { i ->
+            def projectTasks = tasks.subList(
+                (i - 1) * 5, Math.min(i * 5, tasks.size())
+            )
+            def milestones = (1..3).collect { m ->
+                new Milestone(id: (i * 3) + m, name: "M${i}-${m}", 
isCompleted: m <= 2)
+            }
+            new Project(
+                id: i,
+                name: "Project$i",
+                status: statuses[i % statuses.size()],
+                budget: 100000 + (i * 50000),
+                department: departments[i % departments.size()],
+                priority: (i % 10) + 1,
+                tasks: projectTasks,
+                milestones: milestones
+            )
+        }
+    }
+
+    /** Baseline: findAll/collect/groupBy/collectEntries closure chains. */
+    @Benchmark
+    void baselineCollectionClosureChain(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def activeEmployees = employees.findAll { it.isActive }
+            def mapped = activeEmployees.collect { it.toMap() }
+            def byDept = mapped.groupBy { it.dept }
+            def deptStats = byDept.collectEntries { dept, emps ->
+                [dept, [count: emps.size(), avgRating: emps.sum { it.rating } 
/ emps.size()]]
+            }
+            bh.consume(deptStats.size())
+        }
+    }
+
+    /** Collection closure chains with periodic cross-type invalidation. */
+    @Benchmark
+    void collectionClosureChainWithInvalidation(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def activeEmployees = employees.findAll { it.isActive }
+            def mapped = activeEmployees.collect { it.toMap() }
+            def byDept = mapped.groupBy { it.dept }
+            def deptStats = byDept.collectEntries { dept, emps ->
+                [dept, [count: emps.size(), avgRating: emps.sum { it.rating } 
/ emps.size()]]
+            }
+            bh.consume(deptStats.size())
+            if (i % 100 == 0) {
+                PluginConfig.metaClass."helper${i % 5}" = { -> i }
+            }
+        }
+    }
+
+    /** Baseline: spread operator (employees*.salary). */
+    @Benchmark
+    void baselineSpreadOperator(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def names = employees*.firstName
+            def salaries = employees*.salary
+            def ratings = employees*.performanceRating
+            bh.consume(names.size() + salaries.size() + ratings.size())
+        }
+    }
+
+    /** Spread operator with periodic cross-type invalidation. */
+    @Benchmark
+    void spreadOperatorWithInvalidation(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def names = employees*.firstName
+            def salaries = employees*.salary
+            def ratings = employees*.performanceRating
+            bh.consume(names.size() + salaries.size() + ratings.size())
+            if (i % 100 == 0) {
+                PluginConfig.metaClass."helper${i % 5}" = { -> i }
+            }
+        }
+    }
+
+    static class CriteriaBuilder {
+        Map result = [:]
+
+        void eq(String field, Object value) {
+            result[field] = value
+        }
+
+        void gt(String field, Object value) {
+            result["${field}_gt"] = value
+        }
+
+        void nested(String name, @DelegatesTo(CriteriaBuilder) Closure cl) {
+            def inner = new CriteriaBuilder()
+            cl.delegate = inner
+            cl.resolveStrategy = Closure.DELEGATE_FIRST
+            cl()
+            result[name] = inner.result
+        }
+
+        Map build() { result }
+    }
+
+    /** Baseline: 3-level nested closure delegation (GORM criteria pattern). */
+    @Benchmark
+    void baselineNestedClosureDelegation(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def builder = new CriteriaBuilder()
+            builder.nested('project') {
+                eq('status', 'IN_PROGRESS')
+                gt('priority', 5)
+                nested('department') {
+                    eq('name', "Dept${i % 5}")
+                    nested('company') {
+                        eq('active', true)
+                    }
+                }
+            }
+            bh.consume(builder.build().size())
+        }
+    }
+
+    /** Nested closure delegation with periodic cross-type invalidation. */
+    @Benchmark
+    void nestedClosureDelegationWithInvalidation(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def builder = new CriteriaBuilder()
+            builder.nested('project') {
+                eq('status', 'IN_PROGRESS')
+                gt('priority', 5)
+                nested('department') {
+                    eq('name', "Dept${i % 5}")
+                    nested('company') {
+                        eq('active', true)
+                    }
+                }
+            }
+            bh.consume(builder.build().size())
+            if (i % 100 == 0) {
+                PluginConfig.metaClass."helper${i % 5}" = { -> i }
+            }
+        }
+    }
+
+    /** Baseline: GString interpolation with dynamic property access. */
+    @Benchmark
+    void baselineGStringInterpolation(Blackhole bh) {
+        int totalLen = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def emp = employees[i % employees.size()]
+            String full = "${emp.firstName} ${emp.lastName}"
+            String detail = "${emp.jobTitle} at ${emp.department} - 
\$${emp.salary}"
+            String summary = "Employee #${emp.id}: ${full} 
(${emp.performanceRating}/5)"
+            totalLen += full.length() + detail.length() + summary.length()
+        }
+        bh.consume(totalLen)
+    }
+
+    /** GString interpolation with periodic cross-type invalidation. */
+    @Benchmark
+    void gstringInterpolationWithInvalidation(Blackhole bh) {
+        int totalLen = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def emp = employees[i % employees.size()]
+            String full = "${emp.firstName} ${emp.lastName}"
+            String detail = "${emp.jobTitle} at ${emp.department} - 
\$${emp.salary}"
+            String summary = "Employee #${emp.id}: ${full} 
(${emp.performanceRating}/5)"
+            totalLen += full.length() + detail.length() + summary.length()
+            if (i % 100 == 0) {
+                PluginConfig.metaClass."helper${i % 5}" = { -> i }
+            }
+        }
+        bh.consume(totalLen)
+    }
+
+    /** Baseline: dynamic property access by name string. */
+    @Benchmark
+    void baselineDynamicPropertyByName(Blackhole bh) {
+        String[] fields = ['firstName', 'lastName', 'email', 'jobTitle', 
'department']
+        int totalLen = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def emp = employees[i % employees.size()]
+            for (int f = 0; f < fields.length; f++) {
+                def val = emp."${fields[f]}"
+                totalLen += val?.toString()?.length() ?: 0
+            }
+        }
+        bh.consume(totalLen)
+    }
+
+    /** Dynamic property access with periodic cross-type invalidation. */
+    @Benchmark
+    void dynamicPropertyByNameWithInvalidation(Blackhole bh) {
+        String[] fields = ['firstName', 'lastName', 'email', 'jobTitle', 
'department']
+        int totalLen = 0
+        for (int i = 0; i < ITERATIONS; i++) {
+            def emp = employees[i % employees.size()]
+            for (int f = 0; f < fields.length; f++) {
+                def val = emp."${fields[f]}"
+                totalLen += val?.toString()?.length() ?: 0
+            }
+            if (i % 100 == 0) {
+                PluginConfig.metaClass."helper${i % 5}" = { -> i }
+            }
+        }
+        bh.consume(totalLen)
+    }
+
+    /** Baseline: project metrics aggregation (demo app's getProjectMetrics). 
*/
+    @Benchmark
+    void baselineProjectMetrics(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def project = projects[i % projects.size()]
+            def completedTasks = project.tasks.count { it.status == 'DONE' }
+            def totalHours = project.tasks.sum { it.estimatedHours } ?: 0
+            def completedMilestones = project.milestones.count { 
it.isCompleted }
+            def completion = project.tasks.size() > 0 ?
+                (completedTasks / project.tasks.size() * 100) : 0
+            def metrics = [
+                name: project.name,
+                tasks: project.tasks.size(),
+                completed: completedTasks,
+                hours: totalHours,
+                milestones: completedMilestones,
+                completion: completion
+            ]
+            bh.consume(metrics.size())
+        }
+    }
+
+    /** Project metrics with periodic cross-type invalidation. */
+    @Benchmark
+    void projectMetricsWithInvalidation(Blackhole bh) {
+        for (int i = 0; i < ITERATIONS; i++) {
+            def project = projects[i % projects.size()]
+            def completedTasks = project.tasks.count { it.status == 'DONE' }
+            def totalHours = project.tasks.sum { it.estimatedHours } ?: 0
+            def completedMilestones = project.milestones.count { 
it.isCompleted }
+            def completion = project.tasks.size() > 0 ?
+                (completedTasks / project.tasks.size() * 100) : 0
+            def metrics = [
+                name: project.name,
+                tasks: project.tasks.size(),
+                completed: completedTasks,
+                hours: totalHours,
+                milestones: completedMilestones,
+                completion: completion
+            ]
+            bh.consume(metrics.size())
+            if (i % 100 == 0) {
+                PluginConfig.metaClass."helper${i % 5}" = { -> i }
+            }
+        }
+    }
+
+    /** Baseline: full analysis combining all patterns (demo app's 
runComplexAnalysis). */
+    @Benchmark
+    void baselineFullAnalysis(Blackhole bh) {
+        // Employee analysis
+        def activeEmps = employees.findAll { it.isActive }
+        def empNames = activeEmps*.getFullName()
+        def byDept = activeEmps.groupBy { it.department }
+        def deptSummary = byDept.collectEntries { dept, emps ->
+            def avgSalary = emps.sum { it.salary } / emps.size()
+            def topPerformer = emps.max { it.performanceRating }
+            [dept, [count: emps.size(), avgSalary: avgSalary,
+                    top: topPerformer.getFullName()]]
+        }
+
+        // Project metrics
+        def projectSummary = projects.collect { proj ->
+            def done = proj.tasks.count { it.status == 'DONE' }
+            def blocked = proj.tasks.count { it.status == 'BLOCKED' }
+            [name: proj.name, status: proj.status,
+             done: done, blocked: blocked, budget: proj.budget]
+        }
+
+        // Cross-entity: high-priority tasks by department
+        def highPriority = tasks.findAll { it.priority > 7 }
+        def taskSummary = highPriority.groupBy { it.status }
+            .collectEntries { status, tl ->
+                [status, tl.collect { "${it.name} (P${it.priority})" }]
+            }
+
+        bh.consume(deptSummary.size() + projectSummary.size() +
+                   taskSummary.size() + empNames.size())
+    }
+
+    /** Full analysis with cross-type invalidation before and during 
execution. */
+    @Benchmark
+    void fullAnalysisWithInvalidation(Blackhole bh) {
+        // Ongoing framework metaclass activity
+        PluginConfig.metaClass."preRequest${System.nanoTime() % 3}" = { -> 
'init' }
+

Review Comment:
   Good catch - replaced `System.nanoTime()` with a deterministic 
`invalidationCounter` field in the benchmark state. Fixed in 1af1c45.





> Groovy 4 runtime performance on average 2.4x slower than Groovy 3
> -----------------------------------------------------------------
>
>                 Key: GROOVY-10307
>                 URL: https://issues.apache.org/jira/browse/GROOVY-10307
>             Project: Groovy
>          Issue Type: Bug
>          Components: bytecode, performance
>    Affects Versions: 4.0.0-beta-1, 3.0.9
>         Environment: OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 
> (build 11.0.11+9)
> OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)
> WIN10 (tests) / REL 8 (web application)
> IntelliJ 2021.2 
>            Reporter: mgroovy
>            Priority: Major
>         Attachments: groovy_3_0_9_gc.png, groovy_3_0_9_loop2.png, 
> groovy_3_0_9_loop4.png, groovy_3_0_9_mem.png, groovy_4_0_0_b1_loop2.png, 
> groovy_4_0_0_b1_loop4.png, groovy_4_0_0_b1_loop4_gc.png, 
> groovy_4_0_0_b1_loop4_mem.png, 
> groovysql_performance_groovy4_2_xx_yy_zzzz.groovy, loops.groovy, 
> profile3.txt, profile4-loops.txt, profile4.txt, profile4d.txt
>
>
> Groovy 4.0.0-beta-1 runtime performance in our framework is on average 2 to 3 
> times slower compared to using Groovy 3.0.9 (regular i.e. non-INDY)
> * Our complete framework and application code is completely written in 
> Groovy, spread over multiple IntelliJ modules
> ** mixed @CompileDynamic/@TypeChecked and @CompileStatic
> ** No Java classes left in project, i.e. no cross compilation occurs
> * We build using IntelliJ 2021.2 Groovy build process, then run / deploy the 
> compiled class files
> ** We do _not_ use a Groovy based DSL, nor do we execute Groovy scripts 
> during execution
> * Performance degradation when using Groovy 4.0.0-beta-1 instead of Groovy 
> 3.0.9 (non-INDY):
> ** The performance of the largest of our web applications has dropped 3x 
> (startup) / 2x (table refresh) respectively
> *** Stack: Tomcat/Vaadin/Ebean plus framework generated SQL
> ** Our test suite runs about 2.4 times as long as before (120 min when using 
> G4, compared to about 50 min with G3)
> *** JUnit 5 
> *** test suite also contains no scripts / dynamic code execution
> *** Individual test performance varies: A small number of tests runs faster, 
> but the majority is slower, with some extreme cases taking nearly 10x as long 
> to finish
> * Using Groovy 3.0.9 INDY displays nearly identical performance degradation, 
> so it seems that the use of invoke dynamic is somehow at fault



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

Reply via email to