Skip to content

Add benchmark tracking on every push (gate PRs on allocation regressions) - #22

Merged
guy-lud merged 1 commit into
masterfrom
ci/benchmark-tracking
Jul 12, 2026
Merged

Add benchmark tracking on every push (gate PRs on allocation regressions)#22
guy-lud merged 1 commit into
masterfrom
ci/benchmark-tracking

Conversation

@guy-lud

@guy-lud guy-lud commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Saves BenchmarkDotNet results on every push and surfaces the diff on PRs, so a perf/allocation regression is caught before it lands.

How it works

  • Trigger: push to master, PRs to master, and manual workflow_dispatch.
  • Run: dotnet run the benchmark project (ShortRun) with the JSON exporter, filtered to ScanBenchmark + the Q1/Q3/Q4 micro-benchmarks.
  • Extract: jq reshapes BenchmarkDotNet's *-report-full-compressed.json into github-action-benchmark's customSmallerIsBetter format — [{name, unit: "bytes", value}].
  • Track/gate via benchmark-action/github-action-benchmark@v1:
    • push to master → records the new point on a gh-pages data branch (dev/bench).
    • PR → compares against that baseline, comments on a >10% allocation jump, and fails the check.

Key design choice: gate on allocations, not time

Allocated bytes are deterministic, so they're stable on shared GitHub runners and safe to fail a build on. Wall-clock time is far too noisy to gate — it stays in the run logs for eyeballing only. (This is also why the macro ScanBenchmark time didn't move for the quick wins but the micro-benchmark allocations did.)

Notes for review

  • Creates a gh-pages branch (data only, dev/bench/) on the first master run — that's github-action-benchmark's standard store. If you'd rather not have a gh-pages branch, I can switch to external-data-json-path + actions/cache instead. Your call.
  • Ordering: the micro-benchmark filters match nothing until Perf quick wins Q1–Q4 (+ handoff/fix-plan refresh) #21 lands them on master; until then only ScanBenchmark is tracked. No failure in the meantime.
  • CI cost: ShortRun on ScanBenchmark (2000 types) + 3 micro-benchmarks ≈ a few minutes per run; concurrency cancels superseded runs.
  • The JSON→jq→custom-format pipeline was validated locally against ScanBenchmark before authoring.

Runs BenchmarkDotNet on every push to master and on PRs, then feeds per-benchmark
allocated-bytes into benchmark-action/github-action-benchmark:
- push to master records the new baseline on the gh-pages data branch (dev/bench);
- PRs compare against that baseline, comment on a >10% allocation jump, and fail the check.

Gates on allocated bytes rather than time: allocation counts are deterministic and
stable on shared runners, so they can safely fail a build; time stays informational
(in the run logs). JSON export -> jq reshape -> customSmallerIsBetter was validated
locally against ScanBenchmark. Mirrors ci.yml's SDK/cache setup. The micro-benchmark
filters activate automatically once PR #21 lands them on master.
@guy-lud
guy-lud merged commit a20f2f1 into master Jul 12, 2026
2 of 3 checks passed
guy-lud added a commit that referenced this pull request Jul 12, 2026
…23)

* Session wrap: refresh handoff + fix-plan; gitignore BenchmarkDotNet output

- Handoff/fix-plan now reflect #21 merged (Q1–Q4 + M1 collision fix +
  micro-benchmarks, proven 2.7x–32x on repeated paths) and #22 open
  (per-push benchmark tracking, gate on allocation regressions).
- Records the durable facts: gate on allocations not time, gh-pages holds the
  baseline, and the M1 rule (generated impl name is separate from the section name).
- Next priority remains P3.
- gitignore BenchmarkDotNet.Artifacts/ so local benchmark runs don't leave
  untracked output.

* gitignore .claude/settings.local.json (personal SessionStart hook)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant