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.12.tar.gz (457.2 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.12-py3-none-any.whl (84.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pytest_benchmem-0.4.12.tar.gz
  • Upload date:
  • Size: 457.2 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.12.tar.gz
Algorithm Hash digest
SHA256 6444f523ac390f7eed8661438be2c49cb79cecdd2502fa1497885fe46063061f
MD5 4cf8158c4b3ba36e53cb29cda92485a3
BLAKE2b-256 2cef562028752cf5d56f62305ec3f1ae05072c822ed947c454bc45c1f101a8e0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_benchmem-0.4.12.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.12-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_benchmem-0.4.12-py3-none-any.whl
Algorithm Hash digest
SHA256 4976f8b5fcf536a399958c2fd8872d85fdef874659675f3cc23dedd4f68c17b8
MD5 4441803579485674125323f0dd3ac227
BLAKE2b-256 b0e9fbd57f51c60dd004668c230f4bfba20bce037a86222467744e7f22e1ffc2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_benchmem-0.4.12-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