Skip to content

Groovy - invoke dynamic performance problems #15293

@jglapa

Description

@jglapa

Issue description

We upgraded a large and data-intensive application from Grails 6.2.3 to Grails 7 but noticed a significant performance regression reaching 4x on some operations.

We suspect this is related to Groovy 4's "indy" approach and GORM suffering from that more than expected.

It's not noticeable at first sight. I couldn't come up with a simple example where such a big regression would be noticeable so I ended up using an LLM to generate a small application with a method doing something on the data using GORM.

It's a bunch of domains (Company, Department, Employee, Milestone, Project, Skill, Task) with associations and an initial dataset. Uses H2 dev database for simplicity.

I really hope I haven't missed anything obvious (some optimisation or flag, hibernate cache is disabled in both) and this is indeed a Groovy 4 regression.

  • With 50 iterations (and 5 warmup iterations) I observed the same 4x regression.
  • with -Dgroovy.indy.optimize.threshold=0 -Dgroovy.indy.fallback.threshold=0 it's only ~2x as bad but it's not entirely clear how to use that, other combinations didn't yield results as good
  • when looking at flame graphs unsurprisingly Grails 7 is full of org.codehaus.groovy.vmplugin.v8.* where Grails 6 is using org.codehaus.groovy.runtime.*
  • @CompileStatic hasn't been explored as it requires code changes that I would really like to avoid
  • benchmarks were done with :bootWar, embedded tomcat with -Dgrails.env=development but :bootRun is 8x slower than Grails 6, and 3x slower than Grails 7 bootWar - so development mode in IDE is also hit

At the moment we've had to stop and revert the migration as a 4x performance hit is hard to swallow.

From groovy4: https://groovy-lang.org/releasenotes/groovy-4.0.html#Groovy4.0-consolidation

Currently, the Groovy runtime still contains any necessary support for classes compiled using older versions of Groovy. Please use Groovy versions up to 3.x if you need to generate the older style bytecode.

This work was originally planned for Groovy 3.0, but there were numerous places where "indy" code was noticeably slower than "classic" bytecode. We have made numerous speed improvements (starting with [GROOVY-8298](https://issues.apache.org/jira/browse/GROOVY-8298)) and have some ability to tune internal thresholds (search the code base for groovy.indy.optimize.threshold and groovy.indy.fallback.threshold). That work gave us useful speed improvements, but we welcome further feedback to help improve overall performance of the indy bytecode.

Is there any chance this can be further optimised or could Grails 7 potentially work with Groovy 3?

Link to the benchmark applications: https://github.com/jglapa/grails7-performance-regression

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status

Todo

Relationships

None yet

Development

No branches or pull requests

Issue actions