Skip to main content

Lightweight Python microbenchmarking library with Rust core (PyO3)

Project description

blackbox-bench

A lightweight Python microbenchmarking library with a Rust core (PyO3 + maturin). Designed as a "criterion for Python" — minimal harness overhead, statistically rigorous output, and a small CLI that drops into CI.

import blackbox_bench

bench = blackbox_bench.Bench()

@bench.benchmark
def hash_1kb():
    import hashlib
    hashlib.sha256(b"x" * 1024).digest()

bench.run()
bench.report()                # human-readable table
bench.report(format="json", path="results.json")
blackbox-bench results
─────────────────────────────────────────────────────────────────────────────────
Name        Mean   Median  StdDev    Min    Max  Ops/sec          CI 95%  Outliers
─────────────────────────────────────────────────────────────────────────────────
hash_1kb  1.4 µs   1.4 µs  12.0 ns 1.4 µs 1.5 µs  710,221  [1.4 µs, 1.4 µs]        3
─────────────────────────────────────────────────────────────────────────────────

Features

  • Per-batch timing with auto-batch-sizing — sub-microsecond functions are batched until each sample takes ≥5 µs, well above the timer resolution floor.
  • Per-iteration overhead measurement and subtraction — the harness probes itself at startup so reported nanoseconds attribute time to your code, not blackbox-bench.
  • Bootstrap 95% confidence intervals for the mean, plus Tukey or MAD outlier detection.
  • blackbox_bench.black_box(value) — opaque pass-through that the optimiser can't see through.
  • bench.iter_batched(setup, routine) — setup runs once per sample (untimed); routine runs the timed batch.
  • throughput=@bench.benchmark(throughput=1024) reports MB/s alongside ops/sec.
  • params=[...] — parameterised benchmarks; one result per parameter.
  • Opt-in HDR histogramsBench(histogram=True) attaches a percentile-queryable HdrHistogram to each result.
  • blackbox_bench.compare(baseline_json, current_json) — classifies each row as unchanged / regressed / improved / new / removed using CI overlap, not raw change_pct.
  • Four reporters — table, JSON, self-contained HTML (with inline SVG sparklines), JUnit-compatible XML (with a raw alternative).
  • blackbox-bench run --profile — wraps each benchmark in py-spy and emits SVG flamegraphs alongside the results.

Install

pip install blackbox-bench

Pre-built abi3 wheels are published for cpython 3.10 / 3.11 / 3.12 / 3.13 on linux (x86_64 + aarch64), macOS (x86_64 + aarch64), and windows x86_64. Building from source requires a Rust toolchain.

Optional extras:

pip install blackbox-bench[profile]   # adds py-spy for `blackbox-bench run --profile`

Usage

Decorator API

import blackbox_bench

bench = blackbox_bench.Bench(
    warmup=5,
    target_time_ns=1_000_000_000,
    outlier_method="tukey",        # or "mad" / "none"
    overhead_subtract=True,
    histogram=False,
)

@bench.benchmark
def quick(): sum(range(100))

@bench.benchmark(name="hash_kb", throughput=1024, params=[10, 100, 1000])
def hashing(n):
    import hashlib
    hashlib.sha256(b"x" * n).digest()

results = bench.run()
bench.report(format="html", path="results.html")

iter_batched for setup-isolated timing

import random

@bench.benchmark
def sort_random():
    return bench.iter_batched(
        setup=lambda: random.sample(range(1_000), 1_000),
        routine=lambda xs: sorted(xs),
    )

setup runs once per sample and isn't timed; routine is the timed call inside the batch.

Context manager

with bench.measure("payload_build"):
    payload = build_huge_payload()

bench.run()  # appends measured contexts to results

Module-level decorator + CLI

# bench_hashing.py
import blackbox_bench

@blackbox_bench.benchmark
def sha256_1kb():
    import hashlib
    hashlib.sha256(b"x" * 1024).digest()
blackbox-bench run bench_hashing.py --warmup 5 --iterations 100
blackbox-bench run benches/ --format html --output report.html
blackbox-bench run benches/ --save baseline.json
blackbox-bench compare baseline.json current.json   # CI-classified diff

Comparing runs

import json
report = blackbox_bench.compare(
    open("baseline.json").read(),
    open("current.json").read(),
)
for row in report.rows:
    print(row.name, row.classification, row.change_pct)

ComparisonReport.format("xml") emits JUnit with <failure> on regressed rows — drop the file into Jenkins/GitHub Actions test reporters.

Why Rust under the hood

The harness has to stay small relative to the user's function:

  • Tight sampling loopInstant::now() and the per-batch call dispatch are raw FFI (PyObject_CallNoArgs / PyObject_CallObject), skipping PyO3's higher-level wrappers inside the timed window.
  • GIL released during stats — bootstrap CI, Tukey/MAD, and the histogram run inside py.detach(...) so other Python threads aren't blocked while blackbox-bench crunches its samples.
  • Reused scratch buffersmedian, tukey, mad, and bootstrap_ci_mean share two Vecs owned by the Runner; a 100-benchmark suite still allocates only once for the lot.

The criterion benchmarks at benches/rust_internals.rs measure these primitives directly. benches/bench_dogfood.py measures the assembled harness end-to-end (the empty-pass benchmark should report ~0–1 ns after overhead subtraction).

Migrating from 0.1.0

See MIGRATION.md. Most v0.1.0 code only needs an import swap; the one surface that changed without alias is Bench.report(json_output=True)Bench.report(format="json"). For exact v0.1.0 semantics:

from blackbox_bench.legacy import Bench, benchmark, BenchmarkResult

The legacy shim is removed in v1.1.

License

MIT — see LICENSE.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

blackbox_bench-1.0.0-cp310-abi3-win_amd64.whl (224.2 kB view details)

Uploaded CPython 3.10+Windows x86-64

blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (310.7 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (298.0 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

blackbox_bench-1.0.0-cp310-abi3-macosx_11_0_arm64.whl (280.4 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

blackbox_bench-1.0.0-cp310-abi3-macosx_10_12_x86_64.whl (292.8 kB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file blackbox_bench-1.0.0-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for blackbox_bench-1.0.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 0e9a2b42fe54961b17481748bd926f97ebba42e6a4fe65a7f7fa5a0ee9fef5a0
MD5 514b6bd994d24123936a81568ff2703d
BLAKE2b-256 8f9f72c58bbf22c8e7ffa668ed7232396b58297ead66fc6903314eb12e6a108f

See more details on using hashes here.

Provenance

The following attestation bundles were made for blackbox_bench-1.0.0-cp310-abi3-win_amd64.whl:

Publisher: release.yml on quinnjr/blackbox-bench

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

File details

Details for the file blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 79b589e8f81588c792be290d4b73b9ea9434595706a409ce779096597f8a14ab
MD5 537f2cc8b529678010ce9b0620dc586e
BLAKE2b-256 9618a07c2022bbaccf920ca5e8d33e05676f626bd6fe1360082a1a648a3f492d

See more details on using hashes here.

Provenance

The following attestation bundles were made for blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on quinnjr/blackbox-bench

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

File details

Details for the file blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0ba40766f35432cfdbb0ef7f36aa56341a2fcfd36b1ff67a2acadc48da013840
MD5 2b922f0ff7f4e499359fa72fabd03c22
BLAKE2b-256 bc8e0805b0eda8b31d23139d2e45450171fa1e38d5978175c375b6af57626a96

See more details on using hashes here.

Provenance

The following attestation bundles were made for blackbox_bench-1.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on quinnjr/blackbox-bench

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

File details

Details for the file blackbox_bench-1.0.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for blackbox_bench-1.0.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 62f5827959b3e0bf63804a5802c5f5090e71c86c703af96cd7fde303040d1119
MD5 4ec53c36b7bbe259f59ee624eb3ea05d
BLAKE2b-256 341d97dd7da9d43d7351265e138ad21607029163247a13dd5dd723eb655afd0c

See more details on using hashes here.

Provenance

The following attestation bundles were made for blackbox_bench-1.0.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on quinnjr/blackbox-bench

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

File details

Details for the file blackbox_bench-1.0.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for blackbox_bench-1.0.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 fda519baaf672c42c98857232f1527385f00c3aec300d2686a178f630fc197a4
MD5 a13404e8c9709c6f60a66ed60d19acaf
BLAKE2b-256 f1861f6421492e0ce6a50e37a3738efcb170033bb114ea69a6b4a1f08a710426

See more details on using hashes here.

Provenance

The following attestation bundles were made for blackbox_bench-1.0.0-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on quinnjr/blackbox-bench

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