-
Notifications
You must be signed in to change notification settings - Fork 0
165 lines (146 loc) · 5.3 KB
/
perf-bench.yml
File metadata and controls
165 lines (146 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
name: Performance Benchmarks
on:
pull_request:
branches: [main]
paths:
- 'apps/core/**'
- 'apps/prime-mcp/**'
- 'apps/chronis/**'
- 'Cargo.lock'
concurrency:
group: perf-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
criterion:
name: Criterion wall-clock (throughput tracker, non-blocking)
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/core
steps:
- name: Checkout PR
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: "apps/core -> target"
cache-on-failure: true
- name: Run benchmarks (PR)
run: cargo bench --bench performance_benchmarks -- --output-format bencher | tee /tmp/pr-bench.txt
- name: Checkout base branch
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.base.sha }}
clean: false
path: base
- name: Run benchmarks (base)
working-directory: base/apps/core
run: cargo bench --bench performance_benchmarks -- --output-format bencher | tee /tmp/base-bench.txt
- name: Compare benchmarks
uses: benchmark-action/github-action-benchmark@v1
with:
tool: cargo
output-file-path: /tmp/pr-bench.txt
external-data-json-path: /tmp/base-bench.txt
comment-on-alert: true
alert-threshold: "115%"
fail-on-alert: false
summary-always: true
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-always: true
iai-callgrind:
# Deterministic instruction-count regression gate. Runs alongside the
# criterion wall-clock job; unlike criterion's 115% advisory threshold,
# this job fails the build on >3% instruction-count regression.
# See ~/.claude/skills/rust-perf/SKILL.md Phase 3.
name: iai-callgrind (${{ matrix.package }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- package: allsource-core
working-directory: apps/core
- package: allsource-prime
working-directory: apps/prime-mcp
- package: chronis
working-directory: apps/chronis
permissions:
contents: read
pull-requests: write
defaults:
run:
working-directory: ${{ matrix.working-directory }}
steps:
- name: Checkout PR
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install valgrind
run: |
sudo apt-get update
sudo apt-get install -y valgrind
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: "${{ matrix.working-directory }} -> target"
shared-key: iai-perf-${{ matrix.package }}
cache-on-failure: true
- name: Run iai-callgrind and compare to main baseline
id: bench
run: |
set -o pipefail
cargo bench --bench iai -- --save-baseline main | tee /tmp/iai-output.txt
- name: Enforce 3% regression threshold
run: |
python3 - <<'PY'
import pathlib, re, sys
output = pathlib.Path("/tmp/iai-output.txt").read_text()
pattern = re.compile(r"Instructions:.*?Change:\s*([+-]?\d+\.?\d*)%", re.DOTALL)
worst = 0.0
for m in pattern.finditer(output):
pct = float(m.group(1))
if pct > worst:
worst = pct
print(f"worst instruction-count regression: {worst:.2f}% (threshold 3%)")
if worst > 3.0:
print(f"::error::Perf regression {worst:.2f}% exceeds 3% threshold")
sys.exit(1)
PY
- name: Comment iai summary on PR
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('/tmp/iai-output.txt', 'utf8');
const truncated = body.length > 60000 ? body.slice(0, 60000) + '\n... (truncated)' : body;
const marker = '<!-- iai-perf:${{ matrix.package }} -->';
const commentBody = `${marker}\n### iai-callgrind — \`${{ matrix.package }}/iai\`\n\n\`\`\`\n${truncated}\n\`\`\``;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body && c.body.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: commentBody,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody,
});
}