Skip to content

Add --duration flag to perf for time-budgeted benchmarking - #1168

Merged
xieofxie merged 6 commits into
mainfrom
hualxie/perf_add_duration
Jul 27, 2026
Merged

Add --duration flag to perf for time-budgeted benchmarking#1168
xieofxie merged 6 commits into
mainfrom
hualxie/perf_add_duration

Conversation

@xieofxie

Copy link
Copy Markdown
Contributor

Summary

Adds a --duration <seconds> flag to winml perf. When set, the benchmark runs for a wall-clock time budget instead of a fixed --iterations count. After warmup, inference loops until the duration elapses (always running at least one benchmark iteration so stats are never empty).

This is ideal with --monitor, whose PDH hardware counters need time to emit real utilization data — a small fixed iteration count often finishes before the counters produce meaningful samples.

winml perf -m model.onnx --device npu --monitor --duration 15

Details

  • New --duration FLOAT option (must be > 0), plumbed through BenchmarkConfig.duration.
  • _benchmark_indices() helper centralizes the loop: warmup first, then either a fixed iteration count (default) or a timed loop. Used by the single-model, monitored, and per-module paths.
  • Mutually exclusive with --op-tracing (op-tracing runs its own fixed, small iteration count) — rejected with a clear UsageError.
  • Live --monitor chart shows elapsed/total time progress (Time: 12.3/30s) in duration mode instead of an iteration counter.
  • Accurate iteration reporting: in duration mode the reported iteration count reflects the actual number of timed samples rather than the unused --iterations default. For per-module runs (where each module runs a different count under a shared budget), the count is recorded per instance and the top-level value is null; both single-model and module reports also record duration_sec.
  • --duration is added to the genai "ignored flags" warning (genai benchmarks by token generation).

Tests

  • --duration help/forwarding/default, --op-tracing conflict, non-positive rejection.
  • _benchmark_indices iteration mode, timed budget, and at-least-one-iteration guarantee (deterministic via a faked clock).
  • LiveMonitorDisplay time-based vs iteration-based progress.
  • benchmark_info.iterations reports configured count without duration and actual sample count with duration.

All perf suites pass (test_perf_cli, test_perf_module, test_perf_composite, test_ep_monitor): 200 passed, 2 NPU-only skips; ruff clean.

Run the benchmark for a wall-clock budget (seconds) instead of a fixed
--iterations count. After warmup, inference loops until the duration
elapses (always at least one benchmark iteration). Ideal with --monitor,
whose PDH counters need time to emit real utilization data. Rejected with
--op-tracing, which runs its own fixed iteration count.

Works across the single-model, monitored, and per-module paths. The live
--monitor chart shows elapsed/total time progress in duration mode. The
reported iteration count reflects the actual timed sample count instead of
the unused --iterations default.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@xieofxie
xieofxie requested a review from a team as a code owner July 22, 2026 08:41
@xieofxie

Copy link
Copy Markdown
Contributor Author

Needed for #1149

@DingmaomaoBJTU DingmaomaoBJTU left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Took a pass through this — the overall approach looks good. The warmup→timed-loop split, the at-least-one-iteration guarantee, the --op-tracing conflict, and the genai ignored-flag warning are all correct, and the tests cover the important paths.

A few minor nits (none blocking):

perf.py L732-734logger.info("Running benchmark: %d iterations + %d warmup", ...) still logs the fixed iterations count in --duration mode, so it reads e.g. "Running benchmark: 100 iterations + 10 warmup" for a run that's actually time-bounded. Might be worth branching to log the time budget there.

Two more inline below.

Comment thread src/winml/modelkit/commands/perf.py Outdated
Comment thread src/winml/modelkit/commands/_live_chart.py Outdated
Address PR review nits:
- Log duration/warmup (not iterations) at benchmark start in duration mode.
- _run_simple_loop now logs elapsed/total seconds in duration mode instead
  of a meaningless iteration count over total_iterations.
- LiveMonitorDisplay reads the loop's shared _BenchmarkClock start instead of
  self-stamping on first update(), so the time progress bar tracks the exact
  budget the loop stops on rather than lagging by one inference.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@xieofxie

Copy link
Copy Markdown
Contributor Author

Thanks for the review @DingmaomaoBJTU. Addressed all three nits in 6b3f744:

  1. Info log — the "[3] Run benchmark" log now reports %gs duration + %d warmup in duration mode instead of the iteration count.
  2. _run_simple_loop debug progress — logs an elapsed/total-seconds readout in duration mode instead of the meaningless i/total_iterations count (resolved inline).
  3. Live chart clock — added a shared _BenchmarkClock stamped by _benchmark_indices at the warmup boundary; the display reads it instead of self-stamping, so the time bar tracks the exact budget the loop stops on (resolved inline).

Comment thread src/winml/modelkit/commands/perf.py Fixed
The duration-mode loop in _run_simple_loop does not use the yielded index,
so name it _ to satisfy CodeQL's unused-loop-variable check.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@DingmaomaoBJTU DingmaomaoBJTU left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Found one termination bug and one runtime-dispatch inconsistency in the new flag handling; details inline.

Comment thread src/winml/modelkit/commands/perf.py
Comment thread src/winml/modelkit/commands/perf.py Outdated
Hualiang Xie and others added 2 commits July 27, 2026 11:06
…tion

# Conflicts:
#	src/winml/modelkit/commands/_live_chart.py
#	src/winml/modelkit/commands/perf.py
#	tests/unit/session/test_ep_monitor.py
Reject non-finite --duration values (nan/inf) via a Click callback since
FloatRange lets them through, and move the --duration/--op-tracing conflict
check into the WinML-only path so both flags stay non-fatal (ignored) for
winml-genai.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@DingmaomaoBJTU DingmaomaoBJTU left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall well-structured feature with good test coverage. 1 bug (warmup progress bar in duration mode) and several minor suggestions - see inline comments.

Comment thread src/winml/modelkit/commands/_live_chart.py Outdated
Comment thread src/winml/modelkit/commands/perf.py
Comment thread src/winml/modelkit/commands/perf.py
Comment thread src/winml/modelkit/commands/perf.py Outdated
Comment thread src/winml/modelkit/commands/perf.py Outdated
Comment thread src/winml/modelkit/commands/perf.py Outdated
The warmup progress bar scaled by total_iterations, which in duration mode
includes the unused default iteration count, so warmup 5/10 rendered as ~5%.
Split warmup into its own branch in _render_status so it scales by the warmup
count, and make the iteration-mode benchmark bar match its label.

Also: clarify the --duration help text as a minimum budget, note +inf (vs the
range-rejected -inf) in the validation docstring, group --duration with the
other run-control flags in the genai ignored-flags map, and log benchmark-only
progress in the simple loop.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@DingmaomaoBJTU DingmaomaoBJTU left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM! All review comments addressed cleanly:

  • Warmup progress bar bug fixed with correct branch restructuring
  • Progress log now excludes warmup from denominator
  • Help text clarifies minimum-budget semantics
  • Docstring accurately describes +inf/-inf behavior
  • Dict entries regrouped logically
  • Regression test added for the warmup bar fix

Nice work!

@xieofxie
xieofxie merged commit e848eb5 into main Jul 27, 2026
9 checks passed
@xieofxie
xieofxie deleted the hualxie/perf_add_duration branch July 27, 2026 09:10
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.

3 participants