Skip to content

Commit 33466e0

Browse files
author
Gilad Chase
committed
feat: add cairo-metrics benchmark harness + CI tracking
- Introduce `cairo-metrics`: a small CLI that currently checks wall-clock regressions from main. - Persist results in `repo_root/results.db` (git-ignored), currently SQLite keyed by run id (defaults to current git SHA). This enables caching and tracking results across time locally by checking out builds starting from this commit, and having the tool populate the db. The tool then allows comparisons based on the db results. Future work can easily extend this to a stateful DB machine (like RDS), since the DB is behind a trait. - Add a GitHub Actions workflow that benchmarks baseline vs PR, reuses cached baseline results via artifacts (saves ci runtime for multiple PR runs over the same base branch), and posts a “Benchmark Comparison” PR comment, not blocking, the reviewer decided. - Seed initial benchmark suites (corelib + OpenZeppelin): corelib uses local src, and openzepplin is bundeled as a vendored release (not a submodule for simplicity). - Walltime engine is either home-brewed timed comparison of the compiler library, or via `hyperfine` which uses the binary. It uses hyperfine by default if available (need to install with `apt`) otherwise the builtin. This is useful locally, to debug the builtin engine itself (results are similar since hyperfine cancels out shell overhead), and since hyperfine outputs useful statistical anomaly messages. But if hyperfine is a pain to maintain it can be removed.
1 parent 82c9c71 commit 33466e0

23 files changed

Lines changed: 2930 additions & 10 deletions

.github/workflows/benchmarks.yml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
name: Benchmarks
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
merge_group:
8+
types: [checks_requested]
9+
10+
jobs:
11+
# Run cairo-metrics and post a non-blocking comment on the PR if spotted regressions.
12+
benchmark:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: read
16+
pull-requests: write
17+
steps:
18+
- uses: actions/checkout@v6
19+
with:
20+
fetch-depth: '0'
21+
- uses: dtolnay/rust-toolchain@stable
22+
- uses: Swatinem/rust-cache@v2
23+
- name: Get SHAs
24+
id: shas
25+
run: |
26+
if [ "${{ github.event_name }}" = "pull_request" ]; then
27+
BASE_SHA=$(git rev-parse origin/${{ github.base_ref }})
28+
else
29+
BASE_SHA=$(git rev-parse HEAD~1 || echo "")
30+
fi
31+
echo "base_sha=$BASE_SHA" >> $GITHUB_OUTPUT
32+
echo "pr_sha=${{ github.sha }}" >> $GITHUB_OUTPUT
33+
34+
- name: Restore cached baseline
35+
if: steps.shas.outputs.base_sha != ''
36+
id: cache-baseline
37+
uses: actions/cache/restore@v4
38+
with:
39+
path: results.db
40+
key: benchmark-${{ steps.shas.outputs.base_sha }}
41+
42+
- name: Build cairo-metrics
43+
run: cargo build --profile=ci-dev -p cairo-metrics
44+
45+
- name: Benchmark baseline
46+
if: steps.shas.outputs.base_sha != '' && steps.cache-baseline.outputs.cache-hit != 'true'
47+
run: |
48+
git checkout ${{ steps.shas.outputs.base_sha }}
49+
./target/ci-dev/cairo-metrics run ${{ steps.shas.outputs.base_sha }}
50+
git checkout -
51+
52+
- name: Cache baseline results
53+
if: steps.shas.outputs.base_sha != '' && steps.cache-baseline.outputs.cache-hit != 'true'
54+
uses: actions/cache/save@v4
55+
with:
56+
path: results.db
57+
key: benchmark-${{ steps.shas.outputs.base_sha }}
58+
59+
- name: Benchmark current
60+
run: ./target/ci-dev/cairo-metrics run
61+
62+
- name: Cache current results (current can be base of stacked PR)
63+
uses: actions/cache/save@v4
64+
with:
65+
path: results.db
66+
key: benchmark-${{ steps.shas.outputs.pr_sha }}
67+
68+
- name: Compare
69+
if: github.event_name == 'pull_request' && steps.shas.outputs.base_sha != ''
70+
id: compare
71+
run: |
72+
./target/ci-dev/cairo-metrics compare \
73+
${{ steps.shas.outputs.base_sha }} \
74+
${{ steps.shas.outputs.pr_sha }} \
75+
> comparison.txt 2>&1 || true
76+
cat comparison.txt
77+
78+
- name: Post comparison comment
79+
if: github.event_name == 'pull_request' && steps.shas.outputs.base_sha != ''
80+
continue-on-error: true
81+
uses: actions/github-script@v7
82+
with:
83+
script: |
84+
const fs = require('fs');
85+
let output = '';
86+
try {
87+
output = fs.readFileSync('comparison.txt', 'utf8');
88+
} catch (e) { return; }
89+
90+
const body = `## Benchmark Comparison
91+
92+
\`\`\`
93+
${output}
94+
\`\`\`
95+
`;
96+
97+
const comments = await github.rest.issues.listComments({
98+
owner: context.repo.owner,
99+
repo: context.repo.repo,
100+
issue_number: context.issue.number,
101+
});
102+
103+
const botComment = comments.data.find(c =>
104+
c.user.type === 'Bot' && c.body.includes('Benchmark Comparison')
105+
);
106+
107+
if (botComment) {
108+
await github.rest.issues.updateComment({
109+
owner: context.repo.owner,
110+
repo: context.repo.repo,
111+
comment_id: botComment.id,
112+
body
113+
});
114+
} else {
115+
await github.rest.issues.createComment({
116+
owner: context.repo.owner,
117+
repo: context.repo.repo,
118+
issue_number: context.issue.number,
119+
body
120+
});
121+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ node_modules
66
.DS_Store
77
perf.data*
88
flamegraph.svg
9+
results.db*
910

1011
ensure-no_std/Cargo.lock
1112
ensure-no_std/target/

0 commit comments

Comments
 (0)