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

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

daniellansun opened a new pull request, #3228:
URL: https://github.com/apache/groovy/pull/3228

   https://issues.apache.org/jira/browse/GROOVY-11726 reports that 
`GroovyClassLoader.parseClass(GroovyCodeSource, boolean)` roughly doubled in 
wall time for the same script batch between Groovy 3.0.13 and 4.0.24.
   
   ## Measurement
   
   JDK 21.0.12. Representative dynamic scripts (properties, closures, GStrings, 
maps), unique sources so the source cache cannot skip compilation.
   
   Sequential `parseClass` did not show a 2× gap:
   
   | Runtime | Batch | Wall time |
   |---|---|---|
   | Groovy 3.0.13 | 600 scripts, 8 methods | 3527 ms |
   | Groovy 4.0.24 | same | 3779 ms |
   | Groovy 3.0.13, `-Dgroovy.antlr4.cache.threshold=-1` | same | 3090 ms |
   | Groovy 4.0.24, same flag | same | 3360 ms |
   
   Indy on or off, an 80-jar classpath, and those jars on the parent loader 
stayed within a few percent. JFR on both release jars shows the same hot 
methods: lexer ATN simulation, then `ClassNode` work and class generation. 
Groovy 3.0.13's ASM cannot read JDK 21 class files, so `@CompileStatic` against 
JDK classes was not compared. On this master, one thread compiling 80 heavier 
scripts took 967 ms, against 1077 ms for Groovy 4.0.24 on the same harness.
   
   ## What the batch was waiting on
   
   `parseClass` compiled inside `FlexibleCache.getAndPut`. That method holds 
its write lock for the whole value-provider call, so every compilation on one 
`GroovyClassLoader` waited for every other one, including distinct sources. On 
Groovy 4.0.24 a 4-thread batch of 80 scripts took 1101 ms, against 1077 ms on 
one thread.
   
   ## Change
   
   Compile outside that lock. After a cache miss, `doParseClass` runs, then 
`getAndPut` only publishes the class already built; the first publisher for a 
key is kept. `definePackage` tolerates a concurrent definition of the same 
package.
   
   `bugs.Groovy11726` checks a cache hit, an uncached recompile, and that four 
threads overlap inside conversion. Before this change the overlap assertion 
failed with "overlapped 1 at a time".
   
   ## After
   
   Same 80-script harness on this commit: 967 ms on one thread, 690 ms and 680 
ms on four threads. Shared parser DFA state still limits scaling, so four 
threads are not four times faster.
   
   ## Residual risk
   
   Two threads that miss the cache for the same source can both compile; only 
one class is stored. A cycle in which script A loads script B while B loads A 
is no longer serialized by the global lock. Independent batches are the case 
this changes. `IllegalArgumentException` from `definePackage` is ignored only 
when that package is already defined.
   
   `./gradlew :test --tests bugs.Groovy11726 --tests 
groovy.lang.GroovyClassLoaderTest` and `./gradlew lintGate` passed locally.
   
   ### Testing
   - `bugs.Groovy11726`
   




> Performance Regression in GroovyClassLoader.parseClass After Migration to 
> Groovy 4
> ----------------------------------------------------------------------------------
>
>                 Key: GROOVY-11726
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11726
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Puneet Singla
>            Priority: Critical
>              Labels: performance
>
> After upgrading to Groovy 4, we have observed a *significant increase in 
> compilation time* when using the following method from {{GroovyClassLoader}}:
> {code:java}
> public Class parseClass(final GroovyCodeSource codeSource, boolean 
> shouldCacheSource) throws CompilationFailedException
> {code}
> This function was previously performing optimally in Groovy 3.0.13, but with 
> Groovy 4.0.24, compilation latency has *increased significantly.* Earlier it 
> used to take about 4 minutes to compile a bunch of scripts but now it is 
> taking about 9 minutes for the same scripts.
> ----
> *Steps to Reproduce:*
>  # Use {{GroovyClassLoader}} to compile Groovy scripts at runtime using the 
> {{parseClass(GroovyCodeSource, boolean)}} method.
>  # Benchmark the time taken before and after the migration to Groovy 4.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to