Skip to content

feat(codspeed): run the warmup with the simulated cache kept warm - #199

Open
not-matthias wants to merge 1 commit into
mainfrom
cod-3915-warm-cache-warmup
Open

not-matthias wants to merge 1 commit into
mainfrom
cod-3915-warm-cache-warmup

Conversation

@not-matthias

Copy link
Copy Markdown
Member

TLDR: Under Valgrind every measured iteration started from a flushed simulated cache, because CALLGRIND_START_INSTRUMENTATION was sent right before it (and it flushes the cache simulator). The harness now switches instrumentation on once before the warmup and only issues ZERO_STATS before the measured iteration, so the measurement sees the cache state the warmup built up (simulation/memory modes only; walltime is untouched).

  • CodSpeed::warmup_iteration() drives the warmup for bencher, criterion and divan compat: ≥ WARMUP_MIN_ITERATIONS (1), ≤ WARMUP_MAX_ITERATIONS (5), no new iteration after WARMUP_MAX_DURATION (1 s of simulator time).
  • divan compat previously had no warmup at all; iter_manual_unstable now warms up like iter.
  • Measured:/Checked: lines append (warmup: N iterations) so analyses can stratify on the count.
  • Smoke (callgrind, runner cache geometry, 2 MiB working set, 3 runs each, identical counts across runs): strided_walk DLmr 32769 → 0; sort_clone I1mr/ILmr 11 → 0, DLmr 32770 → 0, D1mw/DLmw 1 → 0. Ir unchanged (+4 for the loop condition).

Review notes

  • The dump's summary: line now wraps negative: callgrind derives it from a per-thread baseline that ZERO_STATS only keeps consistent while instrumentation is off. callgrind-parser skips summary: and reads totals:, which is correct under both orders.
  • Warmup iterations now run fully instrumented (slower than the previous uninstrumented warmup), bounded by the 1 s cap.
  • Stacked on feat(divan): run each benchmark in its own process #194.

Part of COD-3915

Under Valgrind, every benchmark was measured from a flushed simulated
cache: the harness ran its unmeasured iterations with instrumentation
off, then issued CALLGRIND_START_INSTRUMENTATION right before the
measured one, which flushes the cache simulator. Each measured iteration
therefore paid an artificial cold-cache phase whose size depends on how
the benchmark's memory happens to be laid out, which is one of the
suspected sources of run-to-run variance in simulation mode.

Reorder the client requests so the cache stays warm:

- START_INSTRUMENTATION is sent once, before the first warmup iteration
- ZERO_STATS alone separates the warmup from the measured iteration; it
  resets the cost centers that end up in the `totals:` line of the dump
  without touching the cache state
- STOP_INSTRUMENTATION + DUMP_STATS_AT after the measured iteration are
  unchanged

The warmup is bounded by iterations and time: at least
WARMUP_MIN_ITERATIONS (1), at most WARMUP_MAX_ITERATIONS (5), and no new
iteration starts after WARMUP_MAX_DURATION (1 s of simulator time). The
bencher, criterion and divan compat layers all drive it through the same
`CodSpeed::warmup_iteration` loop, which also gives divan its first
warmup (it had none) and makes `iter_manual_unstable` warm up like
`iter`. Each `Measured:`/`Checked:` line now reports the warmup
iteration count so analyses can stratify on it.

The `summary:` line of a dump is derived from a per-thread baseline that
ZERO_STATS only keeps consistent while instrumentation is off, so it
now wraps to a negative value; the parser ignores that line and reads
`totals:`, which is correct under both orders.
@greptile-apps

greptile-apps Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes affect benchmark measurements across the Rust compatibility workflows.

The logging requirement must be satisfied before merging; no behavioral blocker was found.

Fix All in Claude CodeFindings

  1. P2 Warmup output bypasses tracing ▶
Fix with agent prompt
### Issue 1
crates/codspeed/src/codspeed.rs:200-204
The expanded benchmark message uses `println!`, but the repository requires Rust messages to use tracing macros instead of `println!` or `eprintln!`. Move this message to `info!` to satisfy the requirement before merging.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Keeps Callgrind instrumentation running from warmup through measurement so the measured iteration can reuse the warmed simulated cache.

  • Measured Rust benchmarks now start with their warmup cache state.
  • The compatibility harnesses warm each benchmark before measuring it.
  • Benchmark run lines now show how many warmup iterations ran.

not-matthias explicitly acknowledged wrapped summary: values in the PR description and stated that the parser ignores summary: and reads totals:.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[First warmup request] --> B[Start instrumentation]
    B --> C[Run warmup iteration]
    C --> D{Another warmup allowed?}
    D -->|Yes| C
    D -->|No| E[Zero counters without flushing cache]
    E --> F[Run measured iteration]
    F --> G[Stop instrumentation and dump results]
    G --> H[Report warmup count]
Loading

Reviews (1) · Last reviewed commit: "feat(codspeed): run the warmup with the ..." · Reviewed by Greptile

Comment on lines +200 to +204
"{}: {}{} (warmup: {} iterations)",
action_str,
self.current_benchmark.to_string_lossy(),
group_str
group_str,
self.warmup_iterations,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Warmup output bypasses tracing

The expanded benchmark message uses println!, but the repository requires Rust messages to use tracing macros instead of println! or eprintln!. Move this message to info! to satisfy the requirement before merging.

Rule Used: Never use println!/eprintln! in Rust; use tracing macros (info!/debug!/warn!/error!). (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/codspeed/src/codspeed.rs
Line: 200-204

Comment:
**Warmup output bypasses tracing**

The expanded benchmark message uses `println!`, but the repository requires Rust messages to use tracing macros instead of `println!` or `eprintln!`. Move this message to `info!` to satisfy the requirement before merging.

**Rule Used:** Never use `println!`/`eprintln!` in Rust; use tracing macros (`info!`/`debug!`/`warn!`/`error!`). ([source](https://app.greptile.com/codspeed/-/custom-context?memory=f04f734b-e556-482b-98b9-64c7ce663663))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex

@codspeed

codspeed Bot commented Oct 20, 2026

Copy link
Copy Markdown

Performance comparison unavailable

The base and head of this comparison were measured with different runner settings, so their benchmark values are not directly comparable.

What changed between base and head:

Re-run the base with the same settings to get a valid performance comparison.


Comparing cod-3915-warm-cache-warmup (061f923) with cod-3799-explore-process-per-benchmark-in-codspeed-rust (fce04a6)

Open in CodSpeed

Base automatically changed from cod-3799-explore-process-per-benchmark-in-codspeed-rust to main October 21, 2026 09:26
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