feat: add --memory flag to measure RAM and VRAM per phase#861
Merged
Conversation
Implement per-phase memory tracking that captures Working Set, Private Bytes, and device (NPU/GPU) memory at each benchmark phase boundary: baseline, post-load, post-compile, and post-inference. Key design decisions: - Default enabled (--no-memory to disable) since snapshots are taken between phases and add zero overhead to latency measurements - Pure ctypes implementation (K32GetProcessMemoryInfo) with no new dependencies - Device memory via single-shot PDH query reusing existing adapter resolution logic - Console output shows a table with per-phase deltas and peak summary - JSON output includes full memory profile under 'memory' key New files: - session/monitor/memory_tracker.py: MemoryTracker, MemorySnapshot, MemoryProfile dataclasses and Windows/Linux memory query functions - tests/unit/session/monitor/test_memory_tracker.py: unit tests
…play Shows the actual memory footprint during inference (steady state) rather than the process lifetime peak which may include transient allocations from model loading or compilation.
Contributor
|
I think we could just enabled it, it doesn't take much time |
Contributor
|
Also consider adding this to --monitor |
…emory-measurement
The test from PR #855 mocks BenchmarkResult but did not set memory_profile=None, causing MagicMock comparison failure in display_console_report.
- Remove snapshot_post_load (load+compile combined as model_load) - Use psutil for RSS with ctypes/proc fallback - Simplify MemorySnapshot to rss_mb + peak_wset_mb + device fields - Add delta breakdown in console: model load / inference / total - Update tests for new 3-phase API (14 tests passing)
Call WinMLSession._init_winml_eps_once() before taking the baseline snapshot so one-time EP DLL loading costs are excluded from the model_load_delta metric.
- Reorder: load → compile → snapshot → generate_inputs → benchmark (matches reference: input alloc now in inference delta, not model load) - Add gc.collect() before baseline and post_compile snapshots - Fix format bug: use :+.1f for deltas (shows -24.8 not +-24.8)
- Remove ctypes/proc fallback (psutil is already a dependency) - Remove peak_wset_mb, peak_delta_mb (not meaningful for inference) - Remove device_shared_mb (unused in display) - Remove MemorySnapshot class (just store raw floats) - Flatten MemoryProfile fields for direct access
Inline RSS measurement directly in PerfBenchmark.run() — just 3 calls to get_rss_mb() and arithmetic. memory_tracker.py is now only two functions (~50 lines). No classes, no state management.
Aligns with mem_ov.py: baseline excludes Python library imports and model build pipeline. model_load_delta renamed to compile_delta since it now only measures ORT session compilation.
Baseline now taken after heavy Python imports (WinMLAutoModel etc.) and EP DLL loading, so model_load_delta measures only model-specific work: reading ONNX, graph optimization, ORT session compilation.
Takes RSS baseline after _load_model() completes but before compile(). This naturally excludes all Python imports, EP DLLs, and build pipeline overhead without fragile pre-import hacks. model_load_delta now measures only ORT session compilation (actual model weights in memory).
xieofxie
reviewed
Jun 16, 2026
- Add comment explaining device memory query position (only grows) - Inline adapter_luid call to reduce intermediate variable
Shows device memory growth per phase so users can see how much the model load vs inference contributes to device allocation.
Device memory uses WDDM PDH counters which only work reliably for GPU (DML). For NPU/QNN they report tiny numbers that don't reflect actual device usage. Simplify to process RSS only.
The WDDM PDH device memory counters are GPU-specific and don't provide meaningful data for NPU workloads.
Remove Device Mem from live chart, hw_monitor properties, and to_dict() output. Update tests accordingly.
This reverts commit c9c9fec.
xieofxie
reviewed
Jun 16, 2026
Query GPU Process Memory PDH counters at same phase boundaries (baseline, after compile, after inference) to report VRAM deltas for model load and inference separately.
Integrate composite model support (_run_sub_models/_run_single) with memory measurement. Memory snapshots now live in _run_single().
--memory flag to winml perf for per-phase memory measurement
xieofxie
approved these changes
Jun 16, 2026
DingmaomaoBJTU
added a commit
that referenced
this pull request
Jun 16, 2026
## Summary Adds end-to-end tests for the \--memory\/\--no-memory\ perf CLI flag introduced in #861. ## Changes - Added \memory\ parameter to \_build_perf_args()\ test helper - \ est_benchmark_cpu_memory\: Verifies \--memory\ produces JSON with \memory\ section containing RSS fields, and console output shows Memory/RAM sections - \ est_benchmark_cpu_no_memory\: Verifies \--no-memory\ excludes memory from both JSON and console output - \ est_benchmark_npu_memory\: Verifies VRAM local/shared fields are populated when running on NPU device ## Stacked on - #861 (perf memory measurement implementation) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This was referenced Jun 23, 2026
timenick
added a commit
that referenced
this pull request
Jun 23, 2026
## Summary `winml perf` crashes on a clean install with `No module named 'psutil'` because `src/winml/modelkit/session/monitor/memory_tracker.py` imports `psutil` at module level, and the perf flow imports that module unconditionally. `psutil` was never declared in `[project].dependencies` — only the dev type stub `types-psutil` is present — so the published wheel's `Requires-Dist` omits it, breaking `winml perf` (and `--monitor` / `--memory`) out-of-the-box for every user. Regression from #861 (`feat: add --memory flag`); `memory_tracker.py` did not exist in v0.1.0, so `winml perf` was unaffected there. Installing `psutil` manually confirms perf/build/`--monitor` otherwise work correctly — the only defect is the missing dependency declaration. ## Change Add `psutil>=7` to `[project].dependencies` (aligns with the existing `types-psutil>=7.2.2` stub). Targeting `release/v0.2.0` directly as a release hotfix; `main` will pick it up via the post-release merge-back. Closes #936
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add
--memory/--no-memoryCLI flag towinml perfthat measures process RAM (RSS) and VRAM (GPU/NPU dedicated + shared) at phase boundaries: baseline → compile → inference.Output
Design
_load_model(), aftercompile(), after benchmark loop)\GPU Process Memorycounters queried at same phase boundaries, reported as local/shared separately--no-memoryto disable). Snapshots are taken between phases and add zero overhead to latency measurementsDisplay changes
Mem→RAM,Device Mem→VRAM->showing deltasFiles changed
src/winml/modelkit/commands/perf.py— memory integration in_run_single(), console display,_resolve_adapter_luid()src/winml/modelkit/session/monitor/memory_tracker.py—get_rss_mb()+get_vram_mb()(~50 lines)src/winml/modelkit/commands/_live_chart.py— rename labelssrc/winml/modelkit/session/monitor/live_display.py— rename labelstests/unit/session/monitor/test_memory_tracker.py— unit tests