This is an automated email from the ASF dual-hosted git repository.

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new a7afd1c4d3 add compiler performance to dashboard
a7afd1c4d3 is described below

commit a7afd1c4d37354823caa0d5e7a3cc97ce7add411
Author: Paul King <[email protected]>
AuthorDate: Thu May 14 23:49:50 2026 +1000

    add compiler performance to dashboard
---
 .github/workflows/groovy-perf-daily.yml            | 102 +++++++++++
 .../groovy/gradle/PerformanceTestSummary.groovy    |  32 +++-
 subprojects/performance/dashboard/index.html       | 194 +++++++++++++++++++++
 .../groovy/perf/CompilerPerformanceTest.java       |   4 +-
 4 files changed, 327 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/groovy-perf-daily.yml 
b/.github/workflows/groovy-perf-daily.yml
new file mode 100644
index 0000000000..82160f6b52
--- /dev/null
+++ b/.github/workflows/groovy-perf-daily.yml
@@ -0,0 +1,102 @@
+# 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.
+
+name: perf-daily
+
+on:
+  schedule:
+    - cron: '30 6 * * *'   # 06:30 UTC daily (after jmh-daily 06:00)
+  workflow_dispatch:
+
+permissions:
+  contents: write         # gh-pages push
+  deployments: write
+  issues: write           # commit comments
+
+jobs:
+  benchmark:
+    runs-on: ubuntu-latest
+    env:
+      DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
+    steps:
+      - uses: actions/checkout@v6
+      - uses: actions/setup-java@v5
+        with:
+          distribution: 'zulu'
+          java-version: 21
+          check-latest: true
+      - uses: 
gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
+
+      # Compiles a fixed set of source files using current + latest 
3.x/4.x/5.x.
+      # Versions tracked are configured in subprojects/performance/build.gradle
+      # via `versions 'current', versions.latest3, versions.latest4, 
versions.latest5`.
+      - name: Compiler performance tests
+        run: ./gradlew :perf:performanceTests
+        timeout-minutes: 120
+
+      # Dashboard: https://apache.github.io/groovy/dev/bench/perf/compiler/
+      # Data is appended to dev/bench/perf/compiler/data.js on the gh-pages 
branch.
+      # To remove a noisy run for commit <BAD_SHA>:
+      #   git checkout gh-pages
+      #   F=dev/bench/perf/compiler/data.js
+      #   sed 's/^window\.BENCHMARK_DATA = //; s/;[[:space:]]*$//' "$F" > 
/tmp/d.json
+      #   jq 'del(.entries[][] | select(.commit.id == "<BAD_SHA>"))' 
/tmp/d.json > /tmp/c.json
+      #   { printf 'window.BENCHMARK_DATA = '; cat /tmp/c.json; printf ';\n'; 
} > "$F"
+      #   git commit -am "Drop noisy perf run for <BAD_SHA>" && git push
+      - name: Publish to dashboard
+        uses: 
benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba
 # v1.22.1
+        with:
+          name: 'Compiler Performance'
+          tool: 'customSmallerIsBetter'
+          output-file-path: 
subprojects/performance/build/performance-results.json
+          benchmark-data-dir-path: dev/bench/perf/compiler
+          gh-pages-branch: gh-pages
+          auto-push: true
+          comment-always: true
+          alert-threshold: '140%'
+          fail-on-alert: false
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+
+      # The action writes a default index.html on first run and leaves it in
+      # place after that. We overlay our custom (4-series, normalised) page
+      # every run, but only commit if it has actually changed.
+      - name: Install custom dashboard page
+        run: |
+          set -euo pipefail
+          SRC=subprojects/performance/dashboard/index.html
+          DEST_DIR=dev/bench/perf/compiler
+          cp "$SRC" /tmp/perf-index.html
+          git fetch origin gh-pages
+          git worktree add /tmp/gh-pages gh-pages
+          mkdir -p "/tmp/gh-pages/$DEST_DIR"
+          if ! cmp -s /tmp/perf-index.html 
"/tmp/gh-pages/$DEST_DIR/index.html"; then
+            cp /tmp/perf-index.html "/tmp/gh-pages/$DEST_DIR/index.html"
+            cd /tmp/gh-pages
+            git config user.name 'github-actions[bot]'
+            git config user.email 
'41898282+github-actions[bot]@users.noreply.github.com'
+            git add "$DEST_DIR/index.html"
+            git commit -m 'Update compiler performance dashboard page'
+            git push origin gh-pages
+          else
+            echo 'Custom index.html already up to date.'
+          fi
+
+      - name: Upload reports-perf-compiler
+        uses: actions/upload-artifact@v7
+        with:
+          name: reports-perf-compiler
+          path: |
+            subprojects/performance/build/performance-results.json
+            subprojects/performance/build/compilation-stats-*.csv
diff --git 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestSummary.groovy
 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestSummary.groovy
index f1d7302dfe..7e52792b51 100644
--- 
a/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestSummary.groovy
+++ 
b/build-logic/src/main/groovy/org/apache/groovy/gradle/PerformanceTestSummary.groovy
@@ -18,9 +18,12 @@
  */
 package org.apache.groovy.gradle
 
+import groovy.json.JsonOutput
 import org.gradle.api.DefaultTask
 import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.TaskAction
@@ -32,6 +35,10 @@ class PerformanceTestSummary extends DefaultTask {
     @PathSensitive(PathSensitivity.RELATIVE)
     final ConfigurableFileCollection csvFiles = 
project.objects.fileCollection()
 
+    @OutputFile
+    final RegularFileProperty jsonReport = project.objects.fileProperty()
+            
.convention(project.layout.buildDirectory.file('performance-results.json'))
+
     @TaskAction
     void summarize() {
         def versions = []
@@ -46,10 +53,10 @@ class PerformanceTestSummary extends DefaultTask {
             }
         }
 
-        versions = versions.sort { ((List) it)[1] }
-        def fastest = ((List) versions[0])[1]
+        def sorted = versions.toSorted { ((List) it)[1] }
+        def fastest = ((List) sorted[0])[1]
         def df = new DecimalFormat("#.##")
-        versions.each { version, mean, stdDev ->
+        sorted.each { version, mean, stdDev ->
             print "Groovy ${sprintf '%-20s', version} Average 
${df.format(mean)}ms ± ${df.format(stdDev)}ms "
             if (mean > fastest) {
                 def diff = 100 * (mean - fastest) / fastest
@@ -57,5 +64,24 @@ class PerformanceTestSummary extends DefaultTask {
             }
             println()
         }
+
+        def json = versions.collect { id, mean, stdDev ->
+            [
+                name : seriesName(id),
+                unit : 'ms',
+                value: mean,
+                range: "±${df.format(stdDev)}".toString(),
+                extra: id,
+            ]
+        }
+        def out = jsonReport.get().asFile
+        out.parentFile.mkdirs()
+        out.text = JsonOutput.prettyPrint(JsonOutput.toJson(json))
+    }
+
+    private static String seriesName(String id) {
+        if (id == 'current') return 'compile@current'
+        def m = id =~ /^(\d+)\./
+        return m.find() ? "compile@groovy-${m.group(1)}" : "compile@${id}"
     }
 }
diff --git a/subprojects/performance/dashboard/index.html 
b/subprojects/performance/dashboard/index.html
new file mode 100644
index 0000000000..8aec88ef61
--- /dev/null
+++ b/subprojects/performance/dashboard/index.html
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Groovy Compiler Performance Dashboard</title>
+<style>
+  :root { color-scheme: light; }
+  body {
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 
Helvetica, Arial, sans-serif;
+    margin: 2em auto;
+    max-width: 1200px;
+    padding: 0 1em;
+    color: #222;
+    background: #fff;
+  }
+  h1 { font-weight: 500; margin-bottom: 0.25em; }
+  .intro { color: #444; line-height: 1.45; margin-bottom: 1.5em; max-width: 
70ch; }
+  .chart-wrap { position: relative; height: 480px; margin-bottom: 1.5em; }
+  .meta { color: #666; font-size: 0.9em; margin-top: 1em; line-height: 1.5; }
+  .meta a { color: #4A8CC2; text-decoration: none; }
+  .meta a:hover { text-decoration: underline; }
+  code { background: #f5f5f7; padding: 0.05em 0.3em; border-radius: 3px; 
font-size: 0.92em; }
+  .empty { color: #888; font-style: italic; }
+</style>
+</head>
+<body>
+<h1>Groovy compiler performance (normalised against current)</h1>
+<p class="intro">
+  Each daily run compiles a fixed set of Groovy source files using 
<code>current</code>
+  (master) and the latest releases of Groovy 3.x, 4.x and 5.x. Every series is 
divided by
+  the <code>current</code> measurement from the same run, so <code>current = 
1.0</code> by
+  construction. Values below 1 mean that version compiles faster than current; 
values above
+  1 mean it compiles slower. The trends show how current drifts relative to 
the released lines.
+</p>
+
+<div class="chart-wrap"><canvas id="ratioChart"></canvas></div>
+
+<div class="meta">
+  <div id="lastUpdate"></div>
+  <div id="latest"></div>
+  <div>
+    Raw data: <a href="data.js">data.js</a> &middot;
+    Generated by <a 
href="https://github.com/benchmark-action/github-action-benchmark";>github-action-benchmark</a>
 &middot;
+    <a href="../../../../">Apache Groovy benchmark home</a>
+  </div>
+</div>
+
+<script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js";></script>
+<script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-adapter-date-fns.bundle.min.js";></script>
+<script src="data.js"></script>
+<script>
+(function () {
+  const SERIES = [
+    { name: 'compile@current',  label: 'current (master)', color: '#1F77B4' },
+    { name: 'compile@groovy-5', label: 'Groovy 5.x',       color: '#2CA02C' },
+    { name: 'compile@groovy-4', label: 'Groovy 4.x',       color: '#FF7F0E' },
+    { name: 'compile@groovy-3', label: 'Groovy 3.x',       color: '#D62728' },
+  ];
+
+  const data = window.BENCHMARK_DATA || {};
+  const suites = data.entries || {};
+  const suiteName = Object.keys(suites)[0];
+  if (!suiteName) {
+    document.querySelector('.chart-wrap').innerHTML =
+      '<p class="empty">No benchmark data yet &mdash; waiting for the first 
<code>perf-daily</code> run.</p>';
+    return;
+  }
+  const runs = suites[suiteName] || [];
+
+  const datasets = SERIES.map(s => ({
+    label: s.label,
+    borderColor: s.color,
+    backgroundColor: s.color,
+    pointRadius: 2,
+    pointHoverRadius: 5,
+    borderWidth: 2,
+    tension: 0.15,
+    spanGaps: true,
+    data: [],
+  }));
+
+  const latestVersions = {};
+  for (const run of runs) {
+    const byName = {};
+    for (const b of (run.benches || [])) byName[b.name] = b;
+    const current = byName['compile@current'];
+    if (!current || !current.value) continue;
+    const x = new Date(run.date || (run.commit && run.commit.timestamp) || 0);
+    SERIES.forEach((s, idx) => {
+      const b = byName[s.name];
+      if (!b || !b.value) return;
+      if (b.extra) latestVersions[s.name] = b.extra;
+      datasets[idx].data.push({
+        x,
+        y: b.value / current.value,
+        absMs: b.value,
+        range: b.range || '',
+        commit: (run.commit && run.commit.id) ? run.commit.id.slice(0, 7) : '',
+        commitUrl: run.commit && run.commit.url,
+        version: b.extra || '',
+      });
+    });
+  }
+
+  const baselineLine = {
+    id: 'baselineLine',
+    afterDatasetsDraw(chart) {
+      const { ctx, chartArea: { left, right }, scales: { y } } = chart;
+      const yPx = y.getPixelForValue(1);
+      ctx.save();
+      ctx.strokeStyle = 'rgba(0,0,0,0.35)';
+      ctx.setLineDash([4, 4]);
+      ctx.lineWidth = 1;
+      ctx.beginPath();
+      ctx.moveTo(left, yPx);
+      ctx.lineTo(right, yPx);
+      ctx.stroke();
+      ctx.restore();
+    }
+  };
+
+  const ctx = document.getElementById('ratioChart').getContext('2d');
+  new Chart(ctx, {
+    type: 'line',
+    data: { datasets },
+    plugins: [baselineLine],
+    options: {
+      responsive: true,
+      maintainAspectRatio: false,
+      interaction: { mode: 'index', intersect: false },
+      scales: {
+        x: {
+          type: 'time',
+          time: { unit: 'day', tooltipFormat: 'yyyy-MM-dd' },
+          title: { display: true, text: 'Run date' },
+        },
+        y: {
+          title: { display: true, text: 'compile time / current  (ratio)' },
+          grid: { color: 'rgba(0,0,0,0.06)' },
+        },
+      },
+      plugins: {
+        legend: { position: 'top' },
+        tooltip: {
+          callbacks: {
+            title(items) {
+              const r = items[0] && items[0].raw;
+              const d = new Date(items[0].parsed.x).toISOString().slice(0, 10);
+              return r && r.commit ? r.commit + '  ' + d : d;
+            },
+            label(item) {
+              const r = item.raw;
+              const ratio = item.parsed.y.toFixed(3);
+              const abs = r.absMs ? r.absMs.toFixed(1) + ' ms' : '';
+              const rng = r.range ? ' ' + r.range : '';
+              const ver = r.version ? '  [' + r.version + ']' : '';
+              return item.dataset.label + ': ' + ratio + ' x  (' + abs + rng + 
')' + ver;
+            },
+          },
+        },
+      },
+    },
+  });
+
+  if (data.lastUpdate) {
+    document.getElementById('lastUpdate').textContent =
+      'Last update: ' + new Date(data.lastUpdate).toISOString().replace('T', ' 
').slice(0, 19) + ' UTC';
+  }
+  const pieces = SERIES
+    .filter(s => latestVersions[s.name])
+    .map(s => s.label + ': ' + latestVersions[s.name]);
+  if (pieces.length) {
+    document.getElementById('latest').textContent = 'Latest versions in chart: 
' + pieces.join(' | ');
+  }
+})();
+</script>
+</body>
+</html>
diff --git 
a/subprojects/performance/src/test/java/org/apache/groovy/perf/CompilerPerformanceTest.java
 
b/subprojects/performance/src/test/java/org/apache/groovy/perf/CompilerPerformanceTest.java
index e4383be4a1..64c4d75224 100644
--- 
a/subprojects/performance/src/test/java/org/apache/groovy/perf/CompilerPerformanceTest.java
+++ 
b/subprojects/performance/src/test/java/org/apache/groovy/perf/CompilerPerformanceTest.java
@@ -30,8 +30,8 @@ import java.util.List;
 
 public class CompilerPerformanceTest {
     private final static String GROOVY_VERSION = GroovySystem.getVersion();
-    private final static int WARMUP = 100;
-    private final static int REPEAT = 500;
+    private final static int WARMUP = 50;
+    private final static int REPEAT = 300;
 
     public static void main(String[] args) throws Exception {
         List<File> sources = new ArrayList<>();

Reply via email to