Skip to content

Commit cfde170

Browse files
committed
try to improve benchmarking
1 parent 87cadc6 commit cfde170

11 files changed

Lines changed: 197 additions & 103 deletions

File tree

.bashrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ path_prepend "$HOME/.cargo/bin"
5252
path_prepend "/usr/lib/qt6/bin"
5353
path_prepend "$HOME/.pixi/bin"
5454
path_prepend "$BUN_INSTALL/bin"
55+
path_prepend "$HOME/go/bin"
5556
path_prepend "$HOME/.local/bin"
5657

5758
if hash nvim 2>/dev/null; then

.github/scripts/benchmark-bash.sh

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,37 @@
33
set -eo pipefail
44

55
OUTPUT=${1:-benchmark-bash-result.json}
6-
ITERATIONS=${ITERATIONS:-20}
6+
WARMUPS=${WARMUPS:-3}
7+
RUNS=${RUNS:-30}
78

89
tmpdir=$(mktemp -d)
910
trap 'rm -rf "$tmpdir"' EXIT
1011

1112
hyperfine \
12-
--warmup 3 \
13-
--runs "$ITERATIONS" \
13+
--warmup "$WARMUPS" \
14+
--runs "$RUNS" \
1415
--shell=none \
1516
--style=basic \
16-
--export-csv "$tmpdir/result.csv" \
17+
--export-json "$tmpdir/result.json" \
1718
'bash -i -c exit'
1819

19-
mean_seconds=$(awk -F, 'NR==2 { gsub(/"/, "", $2); print $2 }' "$tmpdir/result.csv")
20-
mean_ms=$(awk -v m="$mean_seconds" 'BEGIN { printf "%.3f", m * 1000 }')
20+
read -r median_ms stddev_ms min_ms <<<"$(python3 - "$tmpdir/result.json" <<'PY'
21+
import json, sys
22+
with open(sys.argv[1]) as f:
23+
r = json.load(f)["results"][0]
24+
print(f"{r['median']*1000:.3f} {r['stddev']*1000:.3f} {r['min']*1000:.3f}")
25+
PY
26+
)"
2127

22-
echo "Mean: ${mean_ms}ms"
28+
echo "Median: ${median_ms}ms (stddev ${stddev_ms}ms, min ${min_ms}ms)"
2329

2430
cat > "$OUTPUT" <<EOF
2531
[
2632
{
2733
"name": "bash startup time",
2834
"unit": "ms",
29-
"value": $mean_ms
35+
"value": $median_ms,
36+
"range": "±$stddev_ms"
3037
}
3138
]
3239
EOF

.github/scripts/benchmark-nvim.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
3+
set -eo pipefail
4+
5+
OUTPUT=${1:-benchmark-result.json}
6+
WARMUPS=${WARMUPS:-3}
7+
RUNS=${RUNS:-30}
8+
9+
tmpdir=$(mktemp -d)
10+
trap 'rm -rf "$tmpdir"' EXIT
11+
12+
hyperfine \
13+
--warmup "$WARMUPS" \
14+
--runs "$RUNS" \
15+
--shell=none \
16+
--style=basic \
17+
--export-json "$tmpdir/result.json" \
18+
'nvim --headless +qa'
19+
20+
read -r median_ms stddev_ms min_ms <<<"$(python3 - "$tmpdir/result.json" <<'PY'
21+
import json, sys
22+
with open(sys.argv[1]) as f:
23+
r = json.load(f)["results"][0]
24+
print(f"{r['median']*1000:.3f} {r['stddev']*1000:.3f} {r['min']*1000:.3f}")
25+
PY
26+
)"
27+
28+
echo "Median: ${median_ms}ms (stddev ${stddev_ms}ms, min ${min_ms}ms)"
29+
30+
cat > "$OUTPUT" <<EOF
31+
[
32+
{
33+
"name": "nvim startup time",
34+
"unit": "ms",
35+
"value": $median_ms,
36+
"range": "±$stddev_ms"
37+
}
38+
]
39+
EOF
40+
echo "Wrote $OUTPUT"

.github/scripts/benchmark.sh

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env python3
2+
"""Compare a benchmark result against a rolling-median baseline of recent runs.
3+
4+
History format is compatible with the github-action-benchmark
5+
``benchmark-data.json`` layout so the file stays easy to inspect by hand.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import argparse
11+
import datetime
12+
import json
13+
import os
14+
import sys
15+
from pathlib import Path
16+
17+
18+
def median(values: list[float]) -> float:
19+
s = sorted(values)
20+
n = len(s)
21+
return s[n // 2] if n % 2 else (s[n // 2 - 1] + s[n // 2]) / 2
22+
23+
24+
def main() -> int:
25+
p = argparse.ArgumentParser(description=__doc__)
26+
p.add_argument("current", type=Path, help="Path to current result JSON")
27+
p.add_argument("history_dir", type=Path, help="Directory holding benchmark-data.json")
28+
p.add_argument("suite", help="Benchmark suite name (history key)")
29+
p.add_argument("--record", action="store_true", help="Append current result to history")
30+
p.add_argument("--window", type=int, default=10, help="Recent entries to median (default: 10)")
31+
p.add_argument("--threshold", type=float, default=1.5, help="Regression ratio (default: 1.5)")
32+
p.add_argument("--max-entries", type=int, default=200, help="Cap stored history (default: 200)")
33+
args = p.parse_args()
34+
35+
args.history_dir.mkdir(parents=True, exist_ok=True)
36+
history_path = args.history_dir / "benchmark-data.json"
37+
38+
current = json.loads(args.current.read_text())[0]
39+
value = current["value"]
40+
unit = current["unit"]
41+
42+
if history_path.exists():
43+
history = json.loads(history_path.read_text())
44+
else:
45+
history = {"entries": {}}
46+
47+
entries = history["entries"].setdefault(args.suite, [])
48+
recent = entries[-args.window:]
49+
50+
print(f"Suite: {args.suite}")
51+
print(f"Current: {value}{unit}")
52+
53+
if recent:
54+
baseline = median([e["benches"][0]["value"] for e in recent])
55+
ratio = value / baseline
56+
print(f"Baseline (median of last {len(recent)}): {baseline:.3f}{unit}")
57+
print(f"Ratio: {ratio:.2f}x (regression threshold {args.threshold}x)")
58+
if ratio > args.threshold:
59+
print(
60+
f"::warning title=Benchmark regression::{args.suite}: "
61+
f"{value:.2f}{unit} is {ratio:.2f}x the median of the last "
62+
f"{len(recent)} runs ({baseline:.2f}{unit})"
63+
)
64+
else:
65+
print("Baseline: (no history yet — recording without comparison)")
66+
67+
if args.record:
68+
entries.append({
69+
"commit": {"id": os.environ.get("GITHUB_SHA", "")},
70+
"date": int(datetime.datetime.now().timestamp() * 1000),
71+
"tool": "customSmallerIsBetter",
72+
"benches": [{"name": current["name"], "value": value, "unit": unit}],
73+
})
74+
history["entries"][args.suite] = entries[-args.max_entries:]
75+
history_path.write_text(json.dumps(history, indent=2))
76+
print(f"Recorded entry ({len(history['entries'][args.suite])} total) to {history_path}")
77+
else:
78+
print("Not recording (--record not set)")
79+
80+
return 0
81+
82+
83+
if __name__ == "__main__":
84+
sys.exit(main())

.github/workflows/gitguardian.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
steps:
2121
- uses: actions/checkout@v4
2222
with:
23-
fetch-depth: 0 # full history needed to scan past commits
23+
fetch-depth: 0 # full history needed to scan past commits
2424

2525
- name: Run gg-shield
2626
uses: GitGuardian/ggshield-action@v1

.github/workflows/install-dotfiles.yml

Lines changed: 28 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@ on:
99
- '.bashrc'
1010
- '.aliases'
1111
- '.github/workflows/install-dotfiles.yml'
12-
- '.github/scripts/benchmark.sh'
12+
- '.github/scripts/benchmark-nvim.sh'
1313
- '.github/scripts/benchmark-bash.sh'
14+
- '.github/scripts/compare-benchmark.py'
1415
pull_request:
1516
paths:
1617
- 'scripts/bootstrap.sh'
1718
- '.config/nvim/**'
1819
- '.bashrc'
1920
- '.aliases'
2021
- '.github/workflows/install-dotfiles.yml'
21-
- '.github/scripts/benchmark.sh'
22+
- '.github/scripts/benchmark-nvim.sh'
2223
- '.github/scripts/benchmark-bash.sh'
24+
- '.github/scripts/compare-benchmark.py'
2325
schedule:
2426
- cron: '0 3 * * 6'
2527
workflow_dispatch:
@@ -46,7 +48,7 @@ jobs:
4648
- name: Install prerequisites
4749
run: |
4850
apt-get update
49-
apt-get install -y git curl sudo tree xz-utils fontconfig gcc make
51+
apt-get install -y git curl sudo tree xz-utils fontconfig gcc make python3
5052
5153
- name: Create test user
5254
run: |
@@ -282,7 +284,7 @@ jobs:
282284
su - clay -c '
283285
export PATH="$HOME/.pixi/bin:$PATH"
284286
cd ~/dotfiles
285-
bash .github/scripts/benchmark.sh /tmp/result.json
287+
bash .github/scripts/benchmark-nvim.sh /tmp/result.json
286288
cat /tmp/result.json
287289
'
288290
@@ -303,34 +305,21 @@ jobs:
303305
restore-keys: |
304306
${{ runner.os }}-benchmark-history-
305307
306-
- name: Compare against benchmark history (alert on regression)
307-
uses: benchmark-action/github-action-benchmark@v1
308-
with:
309-
name: nvim startup (Ubuntu)
310-
tool: customSmallerIsBetter
311-
output-file-path: /tmp/result.json
312-
external-data-json-path: /tmp/cache/benchmark-data.json
313-
alert-threshold: '150%'
314-
comment-on-alert: true
315-
fail-on-alert: false
316-
alert-comment-cc-users: '@claydugo'
317-
github-token: ${{ secrets.GITHUB_TOKEN }}
318-
319-
- name: Compare bash startup against history (alert on regression)
320-
uses: benchmark-action/github-action-benchmark@v1
321-
with:
322-
name: bash startup (Ubuntu)
323-
tool: customSmallerIsBetter
324-
output-file-path: /tmp/bash-result.json
325-
external-data-json-path: /tmp/cache/benchmark-data.json
326-
alert-threshold: '150%'
327-
comment-on-alert: true
328-
fail-on-alert: false
329-
alert-comment-cc-users: '@claydugo'
330-
github-token: ${{ secrets.GITHUB_TOKEN }}
308+
- name: Compare against rolling baseline (nvim + bash)
309+
env:
310+
RECORD: ${{ github.event_name == 'push' && '--record' || '' }}
311+
GITHUB_SHA: ${{ github.sha }}
312+
run: |
313+
chown -R clay:clay /tmp/cache 2>/dev/null || true
314+
su - clay -c "
315+
export GITHUB_SHA='${GITHUB_SHA}'
316+
cd ~/dotfiles
317+
python3 .github/scripts/compare-benchmark.py /tmp/result.json /tmp/cache 'nvim startup (Ubuntu)' ${RECORD}
318+
python3 .github/scripts/compare-benchmark.py /tmp/bash-result.json /tmp/cache 'bash startup (Ubuntu)' ${RECORD}
319+
"
331320
332321
- name: Save benchmark history
333-
if: always()
322+
if: github.event_name == 'push'
334323
uses: actions/cache/save@v4
335324
with:
336325
path: /tmp/cache
@@ -458,7 +447,7 @@ jobs:
458447
run: |
459448
export PATH="$HOME/.pixi/bin:$PATH"
460449
cd "$HOME/dotfiles"
461-
bash .github/scripts/benchmark.sh /tmp/result.json
450+
bash .github/scripts/benchmark-nvim.sh /tmp/result.json
462451
cat /tmp/result.json
463452
464453
- name: Measure bash startup
@@ -476,34 +465,16 @@ jobs:
476465
restore-keys: |
477466
${{ runner.os }}-benchmark-history-
478467
479-
- name: Compare against benchmark history (alert on regression)
480-
uses: benchmark-action/github-action-benchmark@v1
481-
with:
482-
name: nvim startup (macOS)
483-
tool: customSmallerIsBetter
484-
output-file-path: /tmp/result.json
485-
external-data-json-path: /tmp/cache/benchmark-data.json
486-
alert-threshold: '150%'
487-
comment-on-alert: true
488-
fail-on-alert: false
489-
alert-comment-cc-users: '@claydugo'
490-
github-token: ${{ secrets.GITHUB_TOKEN }}
491-
492-
- name: Compare bash startup against history (alert on regression)
493-
uses: benchmark-action/github-action-benchmark@v1
494-
with:
495-
name: bash startup (macOS)
496-
tool: customSmallerIsBetter
497-
output-file-path: /tmp/bash-result.json
498-
external-data-json-path: /tmp/cache/benchmark-data.json
499-
alert-threshold: '150%'
500-
comment-on-alert: true
501-
fail-on-alert: false
502-
alert-comment-cc-users: '@claydugo'
503-
github-token: ${{ secrets.GITHUB_TOKEN }}
468+
- name: Compare against rolling baseline (nvim + bash)
469+
env:
470+
RECORD: ${{ github.event_name == 'push' && '--record' || '' }}
471+
run: |
472+
cd "$HOME/dotfiles"
473+
python3 .github/scripts/compare-benchmark.py /tmp/result.json /tmp/cache 'nvim startup (macOS)' ${RECORD}
474+
python3 .github/scripts/compare-benchmark.py /tmp/bash-result.json /tmp/cache 'bash startup (macOS)' ${RECORD}
504475
505476
- name: Save benchmark history
506-
if: always()
477+
if: github.event_name == 'push'
507478
uses: actions/cache/save@v4
508479
with:
509480
path: /tmp/cache

.github/workflows/lint.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ jobs:
3131
chmod +x selene
3232
sudo mv selene /usr/local/bin/
3333
34+
- name: Install yamlfmt
35+
run: |
36+
YAMLFMT_VERSION=0.21.0
37+
curl -fsSL "https://github.com/google/yamlfmt/releases/download/v${YAMLFMT_VERSION}/yamlfmt_${YAMLFMT_VERSION}_Linux_x86_64.tar.gz" \
38+
| sudo tar -xz -C /usr/local/bin yamlfmt
39+
sudo chmod +x /usr/local/bin/yamlfmt
40+
3441
- name: Discover shell scripts (by shebang or shellcheck directive)
3542
id: discover
3643
run: |
@@ -85,3 +92,7 @@ jobs:
8592
- name: selene
8693
if: always()
8794
run: selene .
95+
96+
- name: yamlfmt --lint
97+
if: always()
98+
run: yamlfmt -lint

.github/workflows/update-readme.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626

2727
- name: Commit and push
2828
if: steps.changes.outputs.changed == 'true'
29-
run: |
29+
run: |-
3030
git config user.name "github-actions[bot]"
3131
git config user.email "github-actions[bot]@users.noreply.github.com"
3232
git add README.md

0 commit comments

Comments
 (0)