feat: add MBResourceUsage mixin with POSIX getrusage() capture #110
Open
feat: add MBResourceUsage mixin with POSIX getrusage() capture #110
Conversation
Records user/system CPU time, peak RSS (normalised to bytes), minor/major page faults, block I/O ops, and voluntary/involuntary context switches via the stdlib resource module. CLI mode uses RUSAGE_CHILDREN (subprocess resources, before/after delta); Python API mode uses RUSAGE_SELF. maxrss is taken from the post-run snapshot directly (it's a high-water mark, not an accumulator). On Windows the mixin records an empty dict silently. Added as a default CLI mixin in _DEFAULT_MIXINS.
… API modes CLI tests: default inclusion, all 9 fields present and numeric, --no-mixin exclusion, --show-mixins marker, and three real-subprocess tests (maxrss > 0, cpu non-negative, counts non-negative). Python API tests: all 9 fields present, maxrss positive and int, utime/stime non-negative, counts non-negative, and Windows fallback (resource=None records empty dict). POSIX-only tests are marked skipif(sys.platform == 'win32').
- cli.md: update default-mixins paragraph to include resource-usage, add resource-usage options section (no flags, Windows note) - mixins.md: add MBResourceUsage to the reference table, update CLI defaults paragraph, add full MBResourceUsage section (modes, fields table, platform note, macOS zero-counter note, CLI usage) - CHANGELOG.md: add [Unreleased] entry for MBResourceUsage
…n CLI mode - _rusage_to_dict: add include_maxrss kwarg; omit maxrss key when False - _rusage_delta: delta maxrss like all other fields (was: post-run value directly), making multi-iteration CLI runs report honest 0 when a child does not exceed the prior RUSAGE_CHILDREN high-water mark - MBResourceUsage: capture/capturepost methods split on CLI vs Python API mode; Python API omits maxrss because RUSAGE_SELF.maxrss is a lifetime process HWM and cannot isolate a single function call - Comprehensive docstring with per-field mode table and platform quirks - docs/user-guide/mixins.md: rewritten MBResourceUsage section with separate JSON examples per mode, per-field Modes column, and full platform notes (maxrss multi-iteration caveat, inblock/oublock macOS, majflt macOS, os.wait4 follow-up) - CHANGELOG.md: promote [Unreleased] to [2.1.0] - 2026-03-19 - docs/cli.md: minor wording tweak (Python resource module)
- Rename _RUSAGE_FIELDS to _RUSAGE_FIELDS_PYTHON_API (8 fields, no maxrss) - Replace test_resource_usage_python_api_maxrss_positive with test_resource_usage_python_api_maxrss_absent: asserts maxrss key is absent from Python API mode resource_usage records - CLI tests (_RUSAGE_FIELDS with maxrss) unchanged — CLI mode still records maxrss via RUSAGE_CHILDREN
…r-child rusage - CLI mode uses os.wait4() (all POSIX) to record exact rusage for each child process; one entry per timed iteration aligned with call.durations. Falls back to RUSAGE_CHILDREN delta on Windows; maxrss stripped when warmup>0 or iterations>1 to avoid misleading cumulative HWM values. - Python API mode records a single aggregate RUSAGE_SELF delta across all iterations (list always has one entry). Dropped pre/post_run_triggers approach due to MRO ordering issue with MicroBenchBase. - resource_usage changed from dict to list[dict] in all modes. - Tests, docs, and CHANGELOG updated accordingly.
…simplify All platforms with the stdlib resource module also have os.wait4(), so the _HAVE_WAIT4=False + resource-available code path could never fire. - Remove _HAVE_WAIT4 flag from cli/main.py and mixins/system.py - Remove RUSAGE_CHILDREN before/after delta fallback in cli/main.py - Remove maxrss-stripping logic in capturepost_resource_usage - Re-raise os.wait4() errors instead of silently falling back - Rewrite CLI test mocking to use os.wait4() instead of _HAVE_WAIT4=False - Update docs to remove Windows-specific fallback narrative
Without a try/finally, an unexpected BaseException (including KeyboardInterrupt during os.wait4 thread join) would leave the child running as an orphan and leak the stdout/stderr pipe file descriptors. - Wrap the post-Popen block in try/except BaseException to kill and reap the child on any unexpected exception before re-raising - Add a finally block that always closes proc.stdout / proc.stderr - Replace raw iter() in _make_mock_popen and one test with _MockPipe, a minimal closeable wrapper, so the finally block works correctly in tests - Add test_cli_keyboard_interrupt_kills_child: verifies kill()/wait() are called and KeyboardInterrupt propagates - Add test_cli_pipe_fds_closed_after_run: verifies pipe FDs are closed after a normal run
…ndler test_cli_keyboard_interrupt_kills_child covers this branch; the pragma was added before that test existed and should not suppress coverage.
…API mode Add cooperative super() forwarding to MicroBenchBase.pre_run_triggers and post_run_triggers so mixin overrides are reached despite the base class appearing before mixin classes in the MRO. MBResourceUsage uses these hooks to snapshot RUSAGE_SELF before and after each individual call, appending a per-iteration delta to resource_usage rather than a single aggregate across all iterations. resource_usage now aligns index-for-index with call.durations in both CLI and Python API mode. Warmup calls are excluded in both modes.
ea09c0d to
03d132f
Compare
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.
On POSIX, records user/system CPU time, peak RSS (normalised to bytes),
minor/major page faults, block I/O ops, and voluntary/involuntary
context switches via the stdlib resource module.
CLI mode uses os.wait4() which calculates per subprocess run;
Python API mode uses RUSAGE_SELF. maxrss is intentionally
omitted here as it's a high watermark for the entire Python
process lifetime.
On Windows, the mixin records an empty dict silently.
Added as a default CLI mixin in _DEFAULT_MIXINS.