This is an automated email from the ASF dual-hosted git repository. jonnybot pushed a commit to branch INDY-PERF-EXPLORATION in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 7d4eae31c66db413387bdd7f073732f8ecb14c5a Author: Jonny Carter <[email protected]> AuthorDate: Fri Feb 20 17:11:08 2026 -0600 Simply split benchmark jobs in CI by class --- .github/workflows/groovy-performance.yml | 8 ++--- subprojects/performance/build.gradle | 53 ++++++++------------------------ 2 files changed, 16 insertions(+), 45 deletions(-) diff --git a/.github/workflows/groovy-performance.yml b/.github/workflows/groovy-performance.yml index ee9e2b64ed..350ad081f4 100644 --- a/.github/workflows/groovy-performance.yml +++ b/.github/workflows/groovy-performance.yml @@ -20,7 +20,7 @@ on: workflow_dispatch: inputs: benchmark_filter: - description: 'Filter by subpackage name (e.g., dispatch, orm, indy). Leave empty for all.' + description: 'Filter by benchmark class name (e.g., CallsiteBench, PropertyAccess). Leave empty for all.' required: false default: '' compare_baseline: @@ -93,7 +93,7 @@ jobs: needs: build-jmh-jar if: github.event_name != 'pull_request' runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 5 strategy: fail-fast: false matrix: ${{ fromJson(needs.build-jmh-jar.outputs.matrix) }} @@ -116,9 +116,9 @@ jobs: -Dgroovy.target.indy=${{ matrix.indy }} \ -jar "$JAR" \ "${{ matrix.pattern }}" \ - -f 1 -wi 1 \ + -f 3 -wi 3 -i 5 \ -rf json -rff results.json - timeout-minutes: 45 + timeout-minutes: 110 - name: Upload results uses: actions/upload-artifact@v4 diff --git a/subprojects/performance/build.gradle b/subprojects/performance/build.gradle index cdd4d067ef..a6a29445b9 100644 --- a/subprojects/performance/build.gradle +++ b/subprojects/performance/build.gradle @@ -34,9 +34,11 @@ performanceTests { def jmhResultsDir = layout.buildDirectory.dir("results/jmh") -// ============================================================================ -// Threshold Parameter Sweep -// ============================================================================ +jmh { + fork = 3 + warmupIterations = 3 + iterations = 5 +} // ============================================================================ // Profiling Support @@ -112,26 +114,18 @@ tasks.register('jmhGcProfile', JavaExec) { // ============================================================================ /** - * Discover all JMH benchmarks from the fat jar and group them by subpackage - * into appropriately-sized chunks for parallel CI execution. - * - * Outputs build/jmh-groups.json — an array of {group, pattern} objects. + * Discover all JMH benchmarks from the fat jar and produce one group per + * benchmark class. Outputs build/jmh-groups.json for CI matrix generation. */ tasks.register('jmhGroups') { group = 'benchmark' - description = 'Discover JMH benchmarks and output groups as JSON for CI matrix' + description = 'Discover JMH benchmark classes and output groups as JSON for CI matrix' dependsOn 'jmhJar' def outputFile = layout.buildDirectory.file("jmh-groups.json") outputs.file(outputFile) doLast { - // Time estimate: 1 fork × (1 warmup @10s + 1 iteration @10s) + ~5s overhead - def secondsPerMethod = 25 - def maxSecondsPerJob = 600 // 10-minute target - def maxPerGroup = (maxSecondsPerJob / secondsPerMethod) - - // List all benchmarks from the fat jar def benchmarks = providers.javaexec { mainClass = 'org.openjdk.jmh.Main' classpath = files(tasks.named('jmhJar')) @@ -140,33 +134,10 @@ tasks.register('jmhGroups') { .collect { it.trim() } .findAll { it.startsWith('org.apache.groovy.') } - // Group by subpackage: bench.dispatch.* → "dispatch", bench.Foo → "core", plugin.* → "plugin" - def groups = benchmarks.groupBy { fqcn -> - def parts = (fqcn - 'org.apache.groovy.').split('\\.') - parts[0] == 'bench' ? (parts.length >= 4 ? parts[1] : 'core') : parts[0] - }.sort() - - // Build entries, splitting large groups into right-sized chunks by class - def entries = groups.collectMany { name, methods -> - if (methods.size() <= maxPerGroup) { - def pattern = name == 'core' - ? ".*(" + methods.collect { it.split('\\.')[-2] }.unique().sort().join('|') + ").*" - : ".*\\.${name}\\..*" - return [[group: name, pattern: pattern, benchmarks: methods.size()]] - } - methods.groupBy { it.split('\\.')[-2] }.sort() - .inject([[classes: [], count: 0]]) { chunks, cls, meths -> - def last = chunks.last() - if (last.count + meths.size() > maxPerGroup && last.classes) { - chunks << [classes: [cls], count: meths.size()] - } else { - last.classes << cls - last.count += meths.size() - chunks - } - }.withIndex(1).collect { chunk, i -> - [group: "${name}-${i}", pattern: ".*(" + chunk.classes.join('|') + ").*", benchmarks: chunk.count] - } + // One group per benchmark class: "org.apache.groovy.bench.orm.PropertyAccessBench.method" → "PropertyAccessBench" + def entries = benchmarks.groupBy { it[0..it.lastIndexOf('.') - 1] }.sort().collect { fqcn, methods -> + def className = fqcn[fqcn.lastIndexOf('.') + 1..-1] + [group: className, pattern: "${fqcn}.*", benchmarks: methods.size()] } def json = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(entries))
