Skip to main content

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

PyPI Python versions CI Docs Ruff License: MIT

On-demand memory profiling for your benchmarks. pytest-benchmem measures how much memory your code actually uses โ€” and shows you where it goes โ€” on the pytest-benchmark tests you already have. Find the heavy path, fix it, confirm the drop. It's for Python libraries and pipelines where memory is a real constraint: large numpy arrays, pandas frames, solvers, C/Cython/Rust extensions.

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, no test changes. When a number looks heavy, keep the profile and open a flamegraph to see the allocating call paths.

๐Ÿ“– Full documentation

1. Measure โ€” spot the heavy benchmark

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 memray peak-memory pass is folded in to the right of the โ”‚, as a separate, untimed run. peak spreads into min/mean/max; allocated / allocations are opt-in. 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.

2. Find where the memory goes

A peak number tells you which benchmark is heavy, not where the memory goes. Keep the memray profile for the offenders, then render the allocating call paths:

pytest --benchmark-only --benchmark-memory --benchmark-memory-profile profiles/
benchmem flamegraph profiles/ --worst peak --open     # render the heaviest, open it

The .bin is memray's raw capture, so it also feeds memray tree / summary / stats. For a native-backed workload (polars/Rust, numpy/C), add --benchmark-memory-profile-native so the flamegraph attributes memory to the real C/Rust frames instead of one opaque bucket.

3. Confirm the fix

Change the code, re-run, and diff the two runs to see the peak actually dropped:

benchmem compare before.json after.json --columns peak --diff

compare reads pytest-benchmark's own JSON and lays the runs side by side, keyed on the benchmark id โ€” --diff shows a signed ฮ”% per benchmark so the win (or the regression) reads at a glance.

Also available

Newer, less battle-tested than the profiling loop above โ€” feedback welcome:

  • Guard CI against a regression (benchmem compare โ€ฆ --fail-on peak:10%) or against an absolute ceiling (@pytest.mark.benchmem(max_peak="500MiB")).
  • Plot scaling curves straight from your parametrize params (benchmem plot run.json --columns peak), and sweep a metric across installed package versions (benchmem sweep mypkg 1.2 1.3 main).
  • rss โ€” the whole-process physical peak the OOM killer watches โ€” via @pytest.mark.benchmem(isolate=True), for container/capacity questions.

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).

For continuous CI tracking, reach for CodSpeed โ€” history, dashboards, PR annotations. It runs pytest-benchmark's benchmark() too, so the same tests serve both: write the benchmark once, let CodSpeed watch timing in CI and pytest-benchmem profile memory on demand. No rewrite, no second set of benchmarks.

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 profiled, compared, and plotted across inputs and versions.

Install

uv add pytest-benchmem            # the fixture + flag + memray engine + benchmem sweep CLI
uv add "pytest-benchmem[plot]"    # + benchmem compare tables and plot views (pandas, plotly)

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. The measure โ†’ profile โ†’ fix loop is the hardened core; the Also available features are newer and may move before 1.0. See the changelog.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_benchmem-0.4.11.tar.gz (454.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_benchmem-0.4.11-py3-none-any.whl (83.0 kB view details)

Uploaded Python 3

File details

Details for the file pytest_benchmem-0.4.11.tar.gz.

File metadata

  • Download URL: pytest_benchmem-0.4.11.tar.gz
  • Upload date:
  • Size: 454.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pytest_benchmem-0.4.11.tar.gz
Algorithm Hash digest
SHA256 76f914c2b557b15411bf8758fc535fab54271db5791547d79f58e450f557e8fc
MD5 c6429dde25859ba82eb3c56112cebd0d
BLAKE2b-256 5a4cf3f92ca6c5322e1de3fd0711eaf1635f61ae318c4fec3339c37dd7082156

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_benchmem-0.4.11.tar.gz:

Publisher: release.yaml on fluxopt/pytest-benchmem

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_benchmem-0.4.11-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_benchmem-0.4.11-py3-none-any.whl
Algorithm Hash digest
SHA256 d5482af99ce1d3f4d499f4b989d5878bdfa729b37787e57545af3891fcdc9198
MD5 38181d6813676111eea64360084ac385
BLAKE2b-256 ca92c0b7e901451951714ce338d20dd32a29d38c6f624c74ccc7ee12b1c2f0b3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_benchmem-0.4.11-py3-none-any.whl:

Publisher: release.yaml on fluxopt/pytest-benchmem

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page