Skip to content

feat(bench): #205 AC#5 enforce <= 50 ms warm threshold in bench-fastled-examples#228

Merged
zackees merged 1 commit intomainfrom
feat/205-ac5-warm-bench-threshold
May 10, 2026
Merged

feat(bench): #205 AC#5 enforce <= 50 ms warm threshold in bench-fastled-examples#228
zackees merged 1 commit intomainfrom
feat/205-ac5-warm-bench-threshold

Conversation

@zackees
Copy link
Copy Markdown
Member

@zackees zackees commented May 10, 2026

Summary

Closes AC#5 of #205 by turning the previously workflow-dispatch-only fastled-examples bench job into a PR-blocking CI gate on the resolver's warm timings.

AC#5 verbatim: Warm build of the FastLED examples matrix adds <= 50 ms over current fbuild on the library-selection step (measured in bench/fastled-examples).

Pragmatic enforcement: each warm resolve_cached call must complete in <= 50 ms wall-clock. Current developer-hardware numbers are ~1-2 ms warm, so 50 ms gives ~25x headroom — more than enough to absorb CI-runner noise without false positives.

What changed

  • bench/fastled-examples/src/main.rs — adds --max-warm-ms <f64> CLI flag. When set, after the table is printed, any example whose warm_ms > threshold is collected; if the list is non-empty the binary exits 1 with a message listing every breach. The threshold is echoed in the report header and recorded in the JSON report under new max_warm_ms / breaches keys. No new deps. Pre-existing report-only behaviour preserved when the flag is omitted.
  • .github/workflows/bench-205.yml — removes the workflow_dispatch-only gate on the fastled-examples job; adds bench/fastled-examples/** to the path filter; the run step now passes --max-warm-ms 50; renamed job to Bench (fastled-examples — AC#5 <= 50 ms warm).
  • bench/fastled-examples/README.md — adds an "AC#5 enforcement" subsection covering the flag, the 50 ms threshold rationale, CI wiring, and a local-usage example. Updates the existing "CI" section to reflect the gate-on-every-PR behaviour.

Files modified

  • .github/workflows/bench-205.yml
  • bench/fastled-examples/README.md
  • bench/fastled-examples/src/main.rs

Test plan

  • uv run soldr cargo check -p fbuild-bench-fastled-examples — clean
  • uv run soldr cargo clippy --workspace --all-targets -- -D warnings — only pre-existing MSRV-mismatch warnings on main, no new warnings
  • uv run soldr cargo fmt --all -- --check — clean
  • Local bench, --max-warm-ms 50 against C:/Users/niteris/dev/fastled: exit 0, threshold printed in header (- Warm threshold: 50.00 ms), all six examples ~8-10 ms warm
  • Local bench, deliberately too-tight --max-warm-ms 0.001: exit 1, all six examples listed as breaches in the error message and JSON breaches field
  • CI: fastled-examples job runs on this PR (touches bench/fastled-examples/** + workflow itself) and gates on the 50 ms threshold

Refs #205 (using Refs, not Closes — AC#6 cold <= 200 ms on teensy41 is still open).

Claude Code

Summary by CodeRabbit

  • Chores
    • Performance benchmark workflow now enforces a 50ms warm-cache timing threshold on pull requests affecting benchmark code.
    • Benchmark reports now include warm-cache timing breach details.

Review Change Stack

…ed-examples

AC#5 requires the warm-cache library-selection step to stay within +50 ms
of current fbuild on the FastLED examples matrix. The bench-fastled-examples
binary now accepts --max-warm-ms <f64>; when set, it exits 1 (after
printing the full table) if any example's warm timing exceeds the threshold,
listing every breach in both the error message and the JSON report.
The bench-205 fastled-examples workflow job is no longer workflow_dispatch-
only — it runs on every PR touching the resolver crates, the bench dir, or
the workflow itself, and passes --max-warm-ms 50. The 50 ms cap gives ~25x
headroom over current ~1-2 ms warm timings on developer hardware (and ~10 ms
on CI), absorbing runner noise without false positives.

Refs #205

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

📝 Walkthrough

Walkthrough

This PR implements AC#5 warm-cache threshold enforcement for the fastled-examples benchmark. It adds a --max-warm-ms CLI flag to detect examples exceeding warm-cache timing limits, computes breach lists, extends JSON reporting with threshold and breach fields, and wires the threshold check into the CI workflow to fail PRs when breaches occur.

Changes

AC#5 Warm-Cache Threshold Enforcement

Layer / File(s) Summary
CLI Contract & Parsing
bench/fastled-examples/src/main.rs
New --max-warm-ms <f64> CLI flag is documented, and shared flag parsers (parse_path_flag, parse_f64_flag) are added to support both --flag value and --flag=value syntax. Old parse_json_flag is removed.
JSON Report Schema Extension
bench/fastled-examples/src/main.rs
write_json_report function signature now accepts max_warm_ms: Option<f64> and a breaches: &[(String, f64)] slice; JSON output includes both fields alongside existing per-example rows.
Core Threshold Enforcement
bench/fastled-examples/src/main.rs
run() parses --max-warm-ms, prints threshold in report header, computes examples exceeding the threshold, and returns a non-zero exit error with breach summary if any limits are breached.
Workflow Integration & PR Triggering
.github/workflows/bench-205.yml
Workflow now triggers on PRs when bench/fastled-examples/** changes, job metadata reflects AC#5 enforcement, workflow_dispatch-only gating is removed, and bench step is invoked with --max-warm-ms 50.
Documentation
bench/fastled-examples/README.md
CLI usage, AC#5 enforcement section, and CI description are updated to document warm-threshold behavior, threshold enforcement on PR, job failure on breach, and threshold/breach reporting in JSON.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • FastLED/fbuild#218: This PR directly implements the AC#5 warm-cache timing gate specification defined in this issue, adding the --max-warm-ms enforcement and CI integration.

Possibly related PRs

  • FastLED/fbuild#219: This PR extends and builds upon the bench-fastled-examples harness introduced in #219 by adding warm-cache timing enforcement and enabling PR-level triggering.

Poem

🐰 Warm caches now enforced with grace,
Fifty milliseconds set the pace!
Breaches caught and JSON blessed,
AC#5 puts examples to the test!
Pull requests won't pass the gate,
If timings show they're running late.

🚥 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 clearly and specifically describes the main change: enforcing a 50ms warm threshold in the bench-fastled-examples tool as part of AC#5 in issue #205.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/205-ac5-warm-bench-threshold

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 `@bench/fastled-examples/src/main.rs`:
- Around line 288-300: parse_path_flag currently treats a bare "--json" (or
other flag) as absent; change it to fail fast when the flag is present but has
no value: update parse_path_flag to return a Result<Option<PathBuf>, String> (or
another error type used in the crate), detect the case where arg == flag and
return Err("flag '<flag>' requires a value") instead of consuming the next arg
as optional, and keep the existing strip_prefix behavior for "--flag=value".
Finally, update call sites that use parse_path_flag to propagate or handle the
error (e.g., print a clear message and exit) so CLI mistakes produce explicit
failures rather than silently hiding the flag.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 28fbedc3-4b47-47d8-8adf-9efb2945a152

📥 Commits

Reviewing files that changed from the base of the PR and between 0240d9e and 12e41b2.

📒 Files selected for processing (3)
  • .github/workflows/bench-205.yml
  • bench/fastled-examples/README.md
  • bench/fastled-examples/src/main.rs

Comment on lines +288 to 300
fn parse_path_flag(args: &[String], flag: &str) -> Option<PathBuf> {
let prefix = format!("{flag}=");
let mut iter = args.iter().skip(1);
while let Some(arg) = iter.next() {
if arg == "--json" {
if arg == flag {
return iter.next().map(PathBuf::from);
}
if let Some(rest) = arg.strip_prefix("--json=") {
if let Some(rest) = arg.strip_prefix(prefix.as_str()) {
return Some(PathBuf::from(rest));
}
}
None
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fail fast when --json is provided without a value.

parse_path_flag silently treats a trailing --json as “flag absent”, which hides CLI mistakes and can make expected report output disappear without an explicit error.

Proposed fix
-fn parse_path_flag(args: &[String], flag: &str) -> Option<PathBuf> {
+fn parse_path_flag(
+    args: &[String],
+    flag: &str,
+) -> Result<Option<PathBuf>, Box<dyn std::error::Error>> {
     let prefix = format!("{flag}=");
     let mut iter = args.iter().skip(1);
     while let Some(arg) = iter.next() {
         if arg == flag {
-            return iter.next().map(PathBuf::from);
+            let next = iter
+                .next()
+                .ok_or_else(|| format!("{flag} requires a value"))?;
+            if next.is_empty() {
+                return Err(format!("{flag} requires a non-empty path").into());
+            }
+            return Ok(Some(PathBuf::from(next)));
         }
         if let Some(rest) = arg.strip_prefix(prefix.as_str()) {
-            return Some(PathBuf::from(rest));
+            if rest.is_empty() {
+                return Err(format!("{flag} requires a non-empty path").into());
+            }
+            return Ok(Some(PathBuf::from(rest)));
         }
     }
-    None
+    Ok(None)
 }
-    let json_out = parse_path_flag(&args, "--json");
+    let json_out = parse_path_flag(&args, "--json")?;

Also applies to: 94-94

🤖 Prompt for 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.

In `@bench/fastled-examples/src/main.rs` around lines 288 - 300, parse_path_flag
currently treats a bare "--json" (or other flag) as absent; change it to fail
fast when the flag is present but has no value: update parse_path_flag to return
a Result<Option<PathBuf>, String> (or another error type used in the crate),
detect the case where arg == flag and return Err("flag '<flag>' requires a
value") instead of consuming the next arg as optional, and keep the existing
strip_prefix behavior for "--flag=value". Finally, update call sites that use
parse_path_flag to propagate or handle the error (e.g., print a clear message
and exit) so CLI mistakes produce explicit failures rather than silently hiding
the flag.

@zackees zackees merged commit 2e8bc4c into main May 10, 2026
80 of 85 checks passed
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