Memory benchmarking for Python, on the pytest-benchmark suites you already time: a memray peak-memory pass on the same tests, plus param-driven plots and cross-version sweeps.
Project description
pytest-benchmem
pytest-benchmem measures how much memory your code allocates, on the same benchmarks you already time. It is for Python libraries and pipelines where memory is a real constraint: large numpy arrays, pandas frames, solvers, C/Cython/Rust extensions. If you already track performance in CI, you can track memory the same way and fail a PR when the footprint grows.
It builds on pytest-benchmark. Add one flag to an existing suite and a memray peak-memory number lands next to the timings โ same test, same run, one JSON file, no test changes.
๐ Full documentation
Quickstart
Take an existing pytest-benchmark test. You don't change it:
import pytest
@pytest.mark.parametrize("n", [10_000, 100_000, 1_000_000])
def test_sort(benchmark, n):
benchmark(sorted, list(range(n, 0, -1)))
Add --benchmark-memory to the run:
pytest --benchmark-only --benchmark-memory --benchmark-columns=min,mean,median
Name (time in us) Min Mean Median โ peakยทmin (KiB) peakยทmean peakยทmax
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
test_sort[10000] 30.2079 (1.0) 37.3511 (1.0) 38.6250 (1.0) โ 78.12 78.12 78.12
test_sort[100000] 299.2500 (9.91) 404.0027 (10.82) 408.5415 (10.58) โ 781.25 781.25 781.25
test_sort[1000000] 3,667.6250 (121.41) 4,427.5485 (118.54) 4,361.7500 (112.93) โ 7,812.50 7,812.50 7,812.50
The timing table is untouched. The memory pass is folded in to the right of the โ. It runs
as a separate, untimed memray pass: peak spreads into min/mean/max, and allocated /
allocations are opt-in. A fourth metric, rss, is the whole-process physical peak the OOM
killer watches; opt in per test with @pytest.mark.benchmem(isolate=True).
Already rebuild state with benchmark.pedantic(setup=โฆ)? The same setup runs (untracked)
before each memory sample, so stateful benchmarks stay accurate with no extra changes.
Plot across inputs and versions
Your parametrize params become plot axes on their own โ no id parsing, no config. The n
in parametrize("n", [...]) is a numeric x-axis, so benchmem plot draws a scaling curve
from it:
benchmem plot run.json --columns peak # peak vs n
benchmem plot run.json --columns time # the same axis, timing instead
The plots read pytest-benchmark's own JSON, so they work on your timing results whether or
not you ran the memory pass. Point --columns at time, peak, allocated, allocations,
or rss, and facet by any other param or dim:
benchmem plot run.json --columns time --facet node.func
Sweep across installed versions of a package from one command:
benchmem sweep mypkg 1.2.0 1.3.0 main --suite bench/
Compare and gate CI
benchmem compare diffs two runs into a per-benchmark table. It shows time โ peak across
every stat, each cell a relative (ร) multiplier against the best run. Add --fail-on to
fail CI on a regression:
test_build[n=5000]
time (s) time (s) time (s) time (s) time (s) peak (MiB) peak (MiB) peak (MiB) peak (MiB) peak (KiB)
name min max mean median stddev โ min max mean median stddev
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
(base) 2 (1.0) 2.3 (1.0) 2.12 (1.0) 2.08 (1.0) 0.09 (1.0) โ 60.00 (1.0) 60.50 (1.0) 60.23 (1.0) 60.20 (1.0) 210.41 (1.0)
(head) 2.05 (1.02) 2.4 (1.04) 2.18 (1.03) 2.12 (1.02) 0.11 (1.22) โ 72.00 (1.20) 73.20 (1.21) 72.53 (1.20) 72.40 (1.20) 510.86 (2.43)
You can also gate inline during the run with --benchmark-memory-compare-fail. Pass
--benchmark-memory-profile DIR to keep the memray .bin of each offender, and
benchmem flamegraph then shows where the memory grew.
Why memray, and where it sits
memray tracks the allocator directly. It catches the numpy/C-allocation detail that RSS
sampling (ASV's peakmem) misses, and folds out the interpreter baseline. pytest-benchmem
uses pytest-benchmark for timing and reads and writes its JSON. It does not reimplement
timing, a CI dashboard (CodSpeed), or cross-commit history (ASV).
vs pytest-memray: both wrap memray, pointed in opposite directions, and they complement
each other. pytest-memray is a guardrail โ limit_memory and leak detection over the whole
test. pytest-benchmem is a benchmark: only the benchmarked action, measured alongside timing,
then compared, swept, and plotted across inputs and versions.
Install
uv add pytest-benchmem # the fixture + flag + memray engine
uv add "pytest-benchmem[plot]" # + the benchmem plot/compare/sweep CLI (pandas, plotly, typer)
memray is Linux/macOS only; Windows installs cleanly with timing-only (the memory pass raises a clear error there).
Status
Early โ extracted from the linopy benchmark suite. API may move before 1.0; see the changelog.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_benchmem-0.4.9.tar.gz.
File metadata
- Download URL: pytest_benchmem-0.4.9.tar.gz
- Upload date:
- Size: 261.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7a22f778cc90653bddd954ce15b00c1551167b3f5f758835d95a598a8ddb2f7
|
|
| MD5 |
eb2ccf7dd192063976c41b5c81ed3e0f
|
|
| BLAKE2b-256 |
812fd55de3f83233c6266144fba42c2764bbd6aa12a1b9bb21f25610443e7691
|
Provenance
The following attestation bundles were made for pytest_benchmem-0.4.9.tar.gz:
Publisher:
release.yaml on fluxopt/pytest-benchmem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_benchmem-0.4.9.tar.gz -
Subject digest:
a7a22f778cc90653bddd954ce15b00c1551167b3f5f758835d95a598a8ddb2f7 - Sigstore transparency entry: 2205385131
- Sigstore integration time:
-
Permalink:
fluxopt/pytest-benchmem@46bf651256754cbfeff19098ae2f8bc9cf2103e2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/fluxopt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@46bf651256754cbfeff19098ae2f8bc9cf2103e2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pytest_benchmem-0.4.9-py3-none-any.whl.
File metadata
- Download URL: pytest_benchmem-0.4.9-py3-none-any.whl
- Upload date:
- Size: 76.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a383f42065f272134fb4f8c0dbd1ec9a07c19dc9d12d45c5bb8fc2d7c4db0bfd
|
|
| MD5 |
3472631b5330fca40f296a744e71f24b
|
|
| BLAKE2b-256 |
a27a4585e18e68405de659e04f907fd84be47845e1c11826764d64fbe1fc3429
|
Provenance
The following attestation bundles were made for pytest_benchmem-0.4.9-py3-none-any.whl:
Publisher:
release.yaml on fluxopt/pytest-benchmem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_benchmem-0.4.9-py3-none-any.whl -
Subject digest:
a383f42065f272134fb4f8c0dbd1ec9a07c19dc9d12d45c5bb8fc2d7c4db0bfd - Sigstore transparency entry: 2205385140
- Sigstore integration time:
-
Permalink:
fluxopt/pytest-benchmem@46bf651256754cbfeff19098ae2f8bc9cf2103e2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/fluxopt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@46bf651256754cbfeff19098ae2f8bc9cf2103e2 -
Trigger Event:
push
-
Statement type: