Hi Daniel,

1. due to the nature of our org it would alas be very hard to
   impossible to supply a meaningful flame graph at this point.
    1. We also did invest quite a bit of time and effort to create a
       cleaned standalone example of the problematic code parts (still
       quite a large part of our project) the last time around, and
       that alas did not lead to any performance improvements on the
       Groovy side.
2. Also, as I already said, it looks like Groovy is "loosing
   performance everywhere", so we are not sure if supplying the graph
   would help, even in principle.
3. But, most importantly, as Jochen stated in his last reply: "Yes, in
   the end MethodHandles are faster, but only once optimizations have
   run. /And that takes thousands of iterations/. MG for example has
   been hit by this."
    1. So he thinks he already knows what causes the performance
       degradation when using Groovy indy, and already points out some
       way(s) to improve this (independent of the specifics our project)...

Cheers,
mg


Am 08.08.2025 um 17:54 schrieb Daniel Sun:
Could you please provide some flame graphs to help us address the performance 
issue you mentioned.

Cheers,
Daniel Sun

On 2025/08/07 19:24:29 MG wrote:
Hi .+,

since Groovy 5 to my knowledge is the first Groovy which does away with
the old, faster, non-invoke-dynamic call site resolution support, I
wanted to do a quick post on that topic that might be of interest to
larger Groovy projects like ours:

  1. After the initial performance problems in Groovy 3/4 caused
     by invoke-dynamic, for which we were, with some effort, in the end
     able to pinpoint the culprit in our code and implement a workaround,
     we unfortunately discovered another performance drop in a later
     Groovy version.
      1. The drop was less severe than in the previous case, but was
         around a factor of 2 when it appeared.
  2. In this case we were alas not able to find a specific code part that
     caused the drop in performance, but it seemed like Groovy was
     leaking a bit of performance on every level, which in the end added
     up to our main web application running with half speed in certain
     crucial parts, as well as our test suite taking far longer to
     execute fully.
  3. After posting about this on the mailing list we initially waited
     whether Groovy invoke-dynamic performance would improve, but over
     time it became clear that that would most likely not be the case.
  4. Faced with this, we had several options:
      1. Stay on Groovy 4 with non-invoke-dynamic enabled for the
         foreseeable future.
      2. Try if switching to statically compiled Groovy (@CompileStatic)
         would solve our performance woes.
      3. Switch (at least partially) to a different JVM language.
  5. Since we neither wanted to be locked into Groovy 4 forever (for
     obvious reasons), and Groovy is our language of choice (for obvious
     reasons ;-) ), the only real option was trying the @CompileStatic route.
  6. Unfortunately we:
      1. Knew from prior experiences that one cannot just
         add @CompileStatic to every class in a project and expect the
         code to compile / run as expected.
      2. Had decided to switch from @CompileStatic to @TypeChecked a
         while ago, to get the benefits of dynamic calls site resolution
         (while still having compile time checking).
  7. So, since just auto-applying @CompileStatic to our whole project was
     not feasible, we used  a more selective approach, where we only
     auto-apply @CompileStatic to a single one of our modules at a time.
      1. Simple groovyConfig source to achieve that below (feel free to
         include verbatim in your project).
          1. Note: If there is already a
             CompileStatic/CompileDynamic/TypeChecked annotation on the
             class it will be skipped.
      2. If you want to cook your own and you use your own Groovy macros,
         be aware that your Groovy macro module must be excluded in any
         case, or you will get some weird, hard to pinpoint build errors!
  8. We started with the lowest of our library modules, working our way
     up in the dependency chain.
      1. In our case that was groovyutil (basic shared functionality),
         and then groovysql (SQL objects, query building & schema
         evolution functionality).
      2. Both modules took a few days each to compile
         under @CompileStatic & all tests to be green.
  9. Given the speed at which we progressed, turning our whole
     project @CompileStatic would have probably taken a month or so.
10. Luckily our bottom up approach paid off, and once the groovysql
     module was done, performance was indistinguishable
     between invoke-dynamic and non-invoke-dynamic builds! :-)
      1. I might do a follow up "tips / lessons learned" post, as time
         allows...
11. Thanks go out to the Groovy dev(s) who are constantly working to
     improve @CompileStatic support: Partially switching
     to @CompileStatic would definitely have been more hassle a few years
     back! G-)

Cheers,
mg


import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.SourceAwareCustomizer
import org.codehaus.groovy.ast.ClassNode
import java.lang.annotation.Annotation
/
// Enable invoke-dynamic (indy) usage/
configuration.optimizationOptions.indy = true
/
// Auto @CompileStatic/
final hasModuleCls = *{ *ClassNode n, List<String> moduleNames *->
*if(n.packageName === null) { return false }
final packagePathElements = n.packageName.split(/\./)
moduleNames.any *{ *String moduleName *->
*packagePathElements.contains(moduleName) *}
}
*
final hasAnyAnnotationCls = *{ *ClassNode n, List<Class<Annotation>>
annotations *-> *n.annotations.any *{ *AnnotationNode an *->
*an.classNode.class in annotations *} }
*
final compileStaticModules = ['groovyutil', 'groovysql']
final skipModules = ['groovymacro']

final autoCompileStatic = new SourceAwareCustomizer(new
ASTTransformationCustomizer(CompileStatic))
autoCompileStatic.setClassValidator *{ *ClassNode n *->
*!hasModuleCls(n, skipModules) && hasModuleCls(n, compileStaticModules)
&& !hasAnyAnnotationCls(n, [CompileStatic, CompileDynamic, TypeChecked]) *}*
configuration.addCompilationCustomizers(autoCompileStatic)




Am 06.08.2025 um 12:54 schrieb Paul King:
Dear community,

The Apache Groovy team is pleased to announce version 5.0.0-rc-1 of
Apache Groovy.
Apache Groovy is a multi-faceted programming language for the JVM.
Further details can be found at thehttps://groovy.apache.org website and in the
Groovy 5 release notes:https://groovy-lang.org/releasenotes/groovy-5.0.html

This is a pre-release of a new version of Groovy.
We greatly appreciate any feedback you can give us when using this version.

This release includes 9 bug fixes/improvements as outlined in the changelog:
https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12318123&version=12356146

Sources, convenience binaries, downloadable documentation and an SDK
bundle can be found at:https://groovy.apache.org/download.html
We recommend you verify your installation using the information on that page.

Jars are also available within the major binary repositories.

We welcome your help and feedback and in particular want
to thank everyone who contributed to this release.

For more information on how to report problems, and to get involved,
visit the project website athttps://groovy.apache.org/

Best regards,

The Apache Groovy team.

Reply via email to