Skip to content

ci(perf): opt-in perf on PRs, run after merge, adaptive bench sampling - #3651

Merged
hellovai merged 5 commits into
canaryfrom
hellovai/perf-fixes
Jun 3, 2026
Merged

ci(perf): opt-in perf on PRs, run after merge, adaptive bench sampling#3651
hellovai merged 5 commits into
canaryfrom
hellovai/perf-fixes

Conversation

@hellovai

@hellovai hellovai commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

What & why

Two changes to how CodSpeed perf benchmarks run in ci.yaml.

1. Perf is opt-in on PRs, automatic after merge

Previously benchmarks ran on every push to any PR that touched baml_language/. Now:

  • After merge (push to main/canary) and on workflow_dispatch → always run.
  • On PRs → opt-in only. A PR runs benchmarks when its title, body, or any commit message contains RUN_CODSPEED=1, run-perf, or /perf.
  • merge_group → never (already settled on the PR).

A new perf-pr-notice job maintains a single sticky comment on each PR (hidden marker, edited in place — no spam) that either confirms benchmarks were triggered or tells the author how to opt in.

Mechanically: a check_perf step in determine_changes computes a run_perf output; both benchmarks-build and benchmarks-run gate on it.

2. Adaptive sampling — fixes the 30-min CI timeout

The perf job was timing out at timeout-minutes: 30 and getting cancelled mid-run. Not a deadlock — benches completed one-by-one until GitHub killed the job.

Root cause: CodSpeed walltime mode runs a fixed 100 samples/bench across 36 benches, several of them heavy. The long pole is compute::bubble_sort_5k (O(n²) ≈ 12.5M VM iterations): ~14s/sample on the codspeed-macro runner (which is ~5× slower than local) × 100 samples ≈ 23 min for that one bench.

Fix: bound each bench to a ~2s adaptive wall-time budget (DIVAN_MAX_TIME). divan then runs as many samples as fit — ~100 for cheap benches (full statistical power kept), down to 1 for the heavy workloads (whose run-to-run variance is already tiny).

Set in CI via the step env (tunable from the workflow) and defaulted in the bench harness main() so local cargo bench / cargo codspeed run are adaptive too.

Measured (local, cargo codspeed run)

fixed 100 DIVAN_MAX_TIME=2
bubble_sort_5k alone didn't finish in 90s 1 sample, 2.9s
full 36-bench suite → timeout 81s
cheap benches (strings, mixed_ops, parallel_sum) 100 samples still 100
heavy benches (bubble_sort/collatz/fib) 100 samples 1–9

Verified the in-main default applies with no env var set: suite ran 81s, bubble_sort=1 sample, cheap benches=100. Projected CI: ~2 min, down from a >30-min timeout.

Notes

  • Benchmarks remain advisory (continue-on-error, not a required gate) — this never blocks a merge.
  • Remaining long pole is bubble_sort_5k's single ~14s sample on the macro runner (a time budget can't interrupt a sample mid-flight). Shrinking that O(n²) workload is a possible follow-up, but it's shared with the cross-language speedtest corpus, so left as-is.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Performance benchmarks are opt-in for pull requests via case-insensitive markers (e.g., RUN_CODSPEED=1, run-perf, /perf); benchmarks still run automatically on direct pushes and manual workflow triggers.
    • CI now uses a single run/skip decision to control all benchmark jobs for clearer gating.
    • PR receives a single sticky status comment that updates with "benchmarks running" or instructions when skipped.
    • Per-benchmark wall time is capped by default (2s) for faster, more predictable feedback.

RUN_CODSPEED=1 / run-perf

hellovai and others added 2 commits June 2, 2026 18:04
Two changes to how CodSpeed perf benchmarks run:

1. Gating policy. Benchmarks no longer run on every PR push. They run
   automatically after merge (push to main/canary) and on workflow_dispatch;
   on PRs they are opt-in via a `RUN_CODSPEED=1` / `run-perf` / `/perf` marker
   in the PR title, body, or any commit message. A new `perf-pr-notice` job
   keeps a single sticky comment on each PR that either confirms benchmarks
   were triggered or explains how to opt in. merge_group is always skipped.

2. Adaptive sampling. Replace walltime mode's fixed 100 samples/bench with a
   ~2s per-bench wall-time budget (DIVAN_MAX_TIME), so divan runs as many
   samples as fit: ~100 for cheap benches, down to 1 for the heavy
   O(n^2)/1M-iter workloads. At fixed 100 samples the suite blew past the
   30-min timeout — compute::bubble_sort_5k alone is ~14s/sample on the
   (~5x slower than local) codspeed-macro runner, i.e. ~23min for one bench.
   With the budget the full 36-bench suite finishes in ~2min (81s measured
   locally). The budget is set in CI via env and defaulted in the bench
   harness main() so `cargo bench` is adaptive too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jun 3, 2026 1:52am
promptfiddle Ready Ready Preview, Comment Jun 3, 2026 1:52am
promptfiddle2 Ready Ready Preview, Comment Jun 3, 2026 1:52am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2dc01722-73bc-46a7-aaf6-3827c68ee570

📥 Commits

Reviewing files that changed from the base of the PR and between 277fe83 and 9ff494e.

📒 Files selected for processing (1)
  • .github/workflows/ci.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/ci.yaml

📝 Walkthrough

Walkthrough

Exports a new determine_changes.outputs.run_perf via check_perf (auto-enable on push/dispatch, opt-in on PRs via markers), gates benchmark jobs on that output, sets DIVAN_MAX_TIME=2 defaults, and upserts a sticky PR notice showing run/skipped status.

Changes

Performance Benchmark Opt-In Policy

Layer / File(s) Summary
Opt-in perf policy definition and export
.github/workflows/ci.yaml
check_perf step evaluates event type and PR/commit markers (RUN_CODSPEED=1, run-perf, /perf) to set `run=true
Benchmark job gating on policy
.github/workflows/ci.yaml
benchmarks-build (baml) and benchmarks-run jobs now run only when needs.determine_changes.outputs.run_perf == 'true'.
Time budgeting, PR feedback, and benchmark runner defaults
.github/workflows/ci.yaml, baml_language/crates/baml_tests/benches/runtime_benchmark.rs
Adds DIVAN_MAX_TIME: "2" to benchmark job env; Rust benchmark runner sets DIVAN_MAX_TIME="2" when unset; perf-pr-notice upserts a hidden-marker PR comment that toggles between running and skipped + opt-in instructions.

Sequence Diagram

sequenceDiagram
  participant Trigger as Workflow Trigger
  participant CheckPerf as check_perf Step
  participant Determine as determine_changes Job
  participant BuildJob as benchmarks-build Job
  participant RunJob as benchmarks-run Job
  participant PRNotice as perf-pr-notice Job
  Trigger->>CheckPerf: event_name, PR title/body, commits
  CheckPerf->>CheckPerf: detect perf markers / evaluate event type
  CheckPerf->>Determine: outputs.run = true|false
  Determine->>BuildJob: needs.determine_changes.outputs.run_perf
  Determine->>RunJob: needs.determine_changes.outputs.run_perf
  alt run_perf == true
    BuildJob->>RunJob: proceed to build and run benchmarks
    RunJob->>RunJob: execute with DIVAN_MAX_TIME=2
  else run_perf == false
    PRNotice->>PRNotice: upsert sticky PR comment with opt-in instructions
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • BoundaryML/baml#3575: Modifies CodSpeed benchmark job structure and may interact with gating/visibility changes.
  • BoundaryML/baml#3601: Also changes how benchmarks-* jobs are organized (build vs run) and relates to job gating.
  • BoundaryML/baml#3631: Edits CodSpeed benchmark suite configuration; overlaps with benchmark orchestration changes.

Poem

🐰 I sniffed the PR for perf-marked light,
A carrot flag said run the tiny fight,
Two seconds tick for every measured run,
A sticky note states skip or let it stun,
The rabbit hops—benchmarks set and done.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: opt-in perf gating on PRs, automatic benchmarks after merge, and adaptive sampling configuration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hellovai/perf-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🏎️ Performance benchmarks are running for this PR

CodSpeed perf benchmarks were triggered because this PR opted in. Results will appear in the CodSpeed check / dashboard once they finish.

Empty commit to trigger the opt-in perf gate (synchronize event) so the
benchmark path runs end-to-end on this PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle June 3, 2026 01:11 Inactive
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Binary size checks passed

7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
baml-cli Linux 🔒 17.3 MB 7.4 MB file 24.1 MB -6.7 MB (-28.0%) OK
packed-program Linux 🔒 12.7 MB 5.4 MB file 15.1 MB -2.4 MB (-16.1%) OK
baml-cli macOS 🔒 13.2 MB 6.4 MB file 18.2 MB -5.0 MB (-27.4%) OK
packed-program macOS 🔒 9.7 MB 4.7 MB file 11.5 MB -1.8 MB (-16.0%) OK
baml-cli Windows 🔒 14.2 MB 6.5 MB file 19.6 MB -5.4 MB (-27.6%) OK
packed-program Windows 🔒 10.2 MB 4.7 MB file 12.2 MB -2.1 MB (-16.8%) OK
bridge_wasm WASM 11.6 MB 🔒 3.3 MB gzip 3.9 MB -585.6 KB (-15.1%) OK

🔒 = the size this artifact is GATED on (ceiling + delta). Binaries gate on file size (installed binary); WASM gates on gzip (download size). The other size is shown for information only.


Generated by cargo size-gate · workflow run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yaml:
- Around line 718-764: The perf PR notice script should skip attempting to write
comments for forked pull requests to avoid 403s; add a guard at the top of the
script (before calling
github.paginate/github.rest.issues.updateComment/createComment) that checks
whether the PR is from a fork (e.g., inspect
context.payload.pull_request.head.repo.fork or compare
context.payload.pull_request.head.repo.owner.login to context.repo.owner) and,
if true, skip posting/updating the comment (log or set body to a no-op). Ensure
the check runs before using context.issue.number and the github.rest.issues.*
methods so forks don’t call updateComment/createComment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8ba03c7c-435f-4db4-b405-128050c7807c

📥 Commits

Reviewing files that changed from the base of the PR and between 6684aff and 19ebb88.

📒 Files selected for processing (2)
  • .github/workflows/ci.yaml
  • baml_language/crates/baml_tests/benches/runtime_benchmark.rs

Comment thread .github/workflows/ci.yaml Outdated
@codspeed-hq

codspeed-hq Bot commented Jun 3, 2026

Copy link
Copy Markdown

Merging this PR will create unknown performance changes

🆕 36 new benchmarks
⏩ 12 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
🆕 WallTime vm_speedtest_string_string_split_100k N/A 264 ms N/A
🆕 WallTime vm_speedtest_string_substring_slice_long_100k N/A 111.7 ms N/A
🆕 WallTime vm_speedtest_compute_nested_loops_500x500 N/A 55.6 ms N/A
🆕 WallTime vm_speedtest_compute_nested_field_access_500k N/A 620 ms N/A
🆕 WallTime vm_speedtest_concurrency_spawn_fan_out_100 N/A 6.2 ms N/A
🆕 WallTime vm_speedtest_string_contains_medium_literal_100k N/A 62.3 ms N/A
🆕 WallTime vm_speedtest_compute_mixed_ops_5k N/A 9.7 ms N/A
🆕 WallTime vm_speedtest_compute_grid_alloc_1000x100 N/A 79 ms N/A
🆕 WallTime vm_speedtest_classes_method_chain_100k N/A 113 ms N/A
🆕 WallTime vm_speedtest_interfaces_match_dispatch_100k N/A 127.8 ms N/A
🆕 WallTime vm_speedtest_concurrency_shared_array_1w_4r N/A 42.2 ms N/A
🆕 WallTime vm_speedtest_concurrency_parallel_sum_sequential_1m N/A 200.3 ms N/A
🆕 WallTime vm_speedtest_compute_binary_tree_depth_20 N/A 690.1 ms N/A
🆕 WallTime vm_speedtest_string_split_long_literal_1k N/A 9.1 ms N/A
🆕 WallTime vm_speedtest_compute_fib_iterative_100k N/A 1.1 s N/A
🆕 WallTime vm_speedtest_string_split_medium_literal_10k N/A 42.9 ms N/A
🆕 WallTime vm_speedtest_compute_closure_apply_1m N/A 809.2 ms N/A
🆕 WallTime vm_speedtest_compute_collatz_100k N/A 3.4 s N/A
🆕 WallTime vm_speedtest_string_split_short_literal_100k N/A 266.7 ms N/A
🆕 WallTime vm_speedtest_concurrency_spawn_await_x10k N/A 1.1 s N/A
... ... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.


Comparing hellovai/perf-fixes (9ff494e) with canary (26c4329)2

Open in CodSpeed

Footnotes

  1. 12 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on canary (6684aff) during the generation of this report, so 26c4329 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: hellovai <vaibhavtheory@gmail.com>
After-merge perf runs (push to main/canary) previously fired on every push.
Now check_perf diffs the pushed range (before..after) against baml_language/**
and skips the benchmarks when the merge didn't touch it.

Diff the push payload's before/after directly rather than reuse
determine_changes' `code` flag: on a push the merge-base of HEAD and
origin/<branch> collapses to HEAD, so that diff is always empty. before..after
is the real merged range. Falls back to running when before is the zero SHA
(branch creation / unknown previous tip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hellovai
hellovai enabled auto-merge June 3, 2026 02:05
@hellovai
hellovai added this pull request to the merge queue Jun 3, 2026
Merged via the queue into canary with commit 821948a Jun 3, 2026
51 checks passed
@hellovai
hellovai deleted the hellovai/perf-fixes branch June 3, 2026 02:13
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