Skip to content

Commit 40fffae

Browse files
runningcodeclaude
andcommitted
ci(benchmark): Add help task configuration benchmark on PRs
Add a pull_request workflow that benchmarks this project's `help` task with the configuration cache disabled (2 warm-ups, 5 builds), comparing the PR base commit against the head commit in a single gradle-profiler run via the git-checkout mutator. This is separate from the existing duckduckgo benchmark build: it profiles configuration time of this project rather than the runtime cost of applying the plugin to a sample app, and it runs automatically on every PR. Results are uploaded as an artifact and summarized as a sticky PR comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8640676 commit 40fffae

3 files changed

Lines changed: 166 additions & 0 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Benchmark help task configuration
2+
3+
on:
4+
pull_request:
5+
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
8+
cancel-in-progress: true
9+
10+
jobs:
11+
benchmark-help-config:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
pull-requests: write
16+
steps:
17+
- name: Checkout Repo
18+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
19+
with:
20+
# Full history so gradle-profiler's git-checkout can reach both the
21+
# base and head commits of the PR.
22+
fetch-depth: 0
23+
24+
- name: Set up Java
25+
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
26+
with:
27+
distribution: 'temurin'
28+
java-version: '17'
29+
30+
- name: Install Gradle Profiler and benchmark the help task
31+
env:
32+
BASE_REF: ${{ github.event.pull_request.base.sha }}
33+
HEAD_REF: ${{ github.event.pull_request.head.sha }}
34+
run: |
35+
curl -s "https://get.sdkman.io" | bash
36+
source "$HOME/.sdkman/bin/sdkman-init.sh"
37+
sdk install gradleprofiler 0.24.0
38+
gradle-profiler --benchmark \
39+
--scenario-file scripts/benchmark/help-config-cache.scenarios \
40+
--output-dir out/help-config
41+
42+
- name: Upload results
43+
if: always()
44+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
45+
with:
46+
name: help-config-benchmark
47+
path: out/help-config/
48+
49+
- name: Build PR comment
50+
run: python3 scripts/benchmark/help-config-comment.py out/help-config/benchmark.csv comment.md
51+
52+
- name: Post comparison comment
53+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
54+
with:
55+
script: |
56+
const fs = require('fs');
57+
const marker = '<!-- help-config-benchmark -->';
58+
const body = fs.readFileSync('comment.md', 'utf8');
59+
const { owner, repo } = context.repo;
60+
const issue_number = context.issue.number;
61+
const comments = await github.paginate(github.rest.issues.listComments, {
62+
owner, repo, issue_number,
63+
});
64+
const existing = comments.find((c) => c.body.includes(marker));
65+
if (existing) {
66+
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
67+
} else {
68+
await github.rest.issues.createComment({ owner, repo, issue_number, body });
69+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Benchmarks the configuration-phase performance of this project's `help` task
2+
# with the configuration cache disabled, comparing the PR base commit against
3+
# the PR head commit in a single gradle-profiler run via the git-checkout
4+
# mutator.
5+
#
6+
# BASE_REF and HEAD_REF are supplied as environment variables by the CI
7+
# workflow; HOCON falls back to environment variables for ${...} lookups.
8+
#
9+
# `help` configures the whole build while executing essentially nothing, so it
10+
# is a clean configuration-time signal. --no-configuration-cache forces a full
11+
# configuration on every build.
12+
13+
help_base {
14+
title = "help base"
15+
tasks = ["help"]
16+
gradle-args = ["--no-configuration-cache"]
17+
warm-ups = 2
18+
iterations = 5
19+
git-checkout {
20+
build = ${BASE_REF}
21+
cleanup = ${HEAD_REF}
22+
}
23+
}
24+
25+
help_pr {
26+
title = "help PR"
27+
tasks = ["help"]
28+
gradle-args = ["--no-configuration-cache"]
29+
warm-ups = 2
30+
iterations = 5
31+
git-checkout {
32+
build = ${HEAD_REF}
33+
cleanup = ${HEAD_REF}
34+
}
35+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python3
2+
"""Render a gradle-profiler benchmark.csv into a Markdown comparison table.
3+
4+
Usage: help-config-comment.py <benchmark.csv> <output.md>
5+
6+
The CSV has one column per scenario; we read the `scenario` header row for the
7+
titles and the `mean` row for the mean total build time (milliseconds).
8+
"""
9+
import csv
10+
import sys
11+
12+
MARKER = "<!-- help-config-benchmark -->"
13+
14+
15+
def main(csv_path: str, out_path: str) -> None:
16+
header = None
17+
mean = None
18+
with open(csv_path, newline="") as f:
19+
for row in csv.reader(f):
20+
if not row:
21+
continue
22+
if row[0] == "scenario":
23+
header = row
24+
elif row[0] == "mean":
25+
mean = row
26+
27+
if header is None or mean is None:
28+
body = f"{MARKER}\n### `help` configuration benchmark\n\nCould not parse benchmark results."
29+
with open(out_path, "w") as f:
30+
f.write(body)
31+
return
32+
33+
titles = header[1:]
34+
means = [float(v) for v in mean[1 : 1 + len(titles)]]
35+
by_title = dict(zip(titles, means))
36+
37+
base = by_title.get("help base")
38+
pr = by_title.get("help PR")
39+
40+
lines = [
41+
MARKER,
42+
"### `help` configuration benchmark (configuration cache disabled)",
43+
"",
44+
"Mean of 5 builds after 2 warm-ups.",
45+
"",
46+
"| Scenario | Mean build time |",
47+
"| --- | --- |",
48+
f"| Base (`help base`) | {base:.0f} ms |",
49+
f"| PR (`help PR`) | {pr:.0f} ms |",
50+
]
51+
52+
delta = pr - base
53+
pct = (delta / base) * 100 if base else 0
54+
sign = "🔺" if delta > 0 else "✅"
55+
lines.append(f"| **Difference** | {sign} {delta:+.0f} ms ({pct:+.1f}%) |")
56+
57+
with open(out_path, "w") as f:
58+
f.write("\n".join(lines) + "\n")
59+
60+
61+
if __name__ == "__main__":
62+
main(sys.argv[1], sys.argv[2])

0 commit comments

Comments
 (0)