A tiny, precise microbenchmarking framework for Python
Project description
PyBench — precise microbenchmarks for Python
Measure small, focused snippets with minimal boilerplate, auto-discovery, smart calibration, and a clean CLI (pybench).
Run benchmarks with one command:
pybench run examples/ [-k keyword] [-P key=value ...]
✨ Highlights
- Simple API: use the
@bench(...)decorator or suites withBench+BenchContext.start()/end()to isolate the hot path. - Auto-discovery:
pybench run <dir>expands to**/*bench.py. - Powerful parameterization: generate Cartesian products with
params={...}or define per-caseargs/kwargs. - On-the-fly overrides:
-P key=valueadjustsn,repeat,warmup,group, or custom params without editing code. - Solid timing model: monotonic clock, warmup, GC control, and context fast-paths.
- Smart calibration: per-variant iteration tuning to hit a target budget.
- Rich reports: aligned tables with percentiles, iter/s, min…max, baseline markers, and speedups vs. base.
- HTML charts: export benchmarks as self-contained Chart.js dashboards with
--export chart. - History tooling: runs auto-save to
.pybenchx/; list, inspect stats, clean, or compare with--vs {name,last}.
🚀 Quickstart
📦 Install
- pip
pip install pybenchx
- uv
uv pip install pybenchx
🧪 Example benchmark
See examples/strings_bench.py for both styles:
from pybench import bench, Bench, BenchContext
@bench(name="join", n=1000, repeat=10)
def join(sep: str = ","):
sep.join(str(i) for i in range(100))
suite = Bench("strings")
@suite.bench(name="join-baseline", baseline=True)
def join_baseline(b: BenchContext):
s = ",".join(str(i) for i in range(50))
b.start(); _ = ",".join([s] * 5); b.end()
🏎️ Running
- Run all examples
pybench run examples/
- Filter by name
pybench run examples/ -k join
- Override params at runtime
pybench run examples/ -P repeat=5 -P n=10000
🎛️ Key CLI options
- Disable color
pybench run examples/ --no-color
- Sorting
pybench run examples/ --sort time --desc
- Time budget per variant (calibration)
pybench run examples/ --budget 300ms # total per variant; split across repeats pybench run examples/ --max-n 1000000 # cap calibrated n
- Profiles
pybench run examples/ --profile thorough # ~1s budget, repeat=30 pybench run examples/ --profile smoke # no calibration, repeat=3 (default)
- Save / Compare / Export
pybench run examples/ --save latest pybench run examples/ --save-baseline main pybench run examples/ --compare main --fail-on mean:7%,p99:12% pybench run examples/ --export chart # HTML dashboard (Chart.js) pybench run examples/ --export json # JSON next to auto-saved run
🗂️ Manage history & baselines
- List everything under
.pybenchx/pybench list pybench list --baselines
- Storage stats & cleanup
pybench stats pybench clean --keep 50
- Compare quickly
pybench run examples/ --vs main # named baseline pybench run examples/ --vs last # last auto-saved run
📊 Output
Header includes CPU, Python, perf_counter clock info, total time, and profile. Table shows speed vs baseline with percent:
(pybench) $ pybench run examples/
cpu: x86_64
runtime: python 3.13.5 (x86_64-linux) | perf_counter: res=1.0e-09s, mono=True
time: 23.378s | profile: smoke, budget=-, max-n=1000000, sequential
benchmark time (avg) iter/s (min … max) p75 p99 p995 vs base
join 13.06 µs 76.6 K 13.00 µs … 13.21 µs 13.08 µs 13.20 µs 13.21 µs -
join_param[n=100,sep='-'] 13.17 µs 75.9 K 12.79 µs … 13.72 µs 13.37 µs 13.70 µs 13.71 µs -
join_param[n=100,sep=':'] 13.06 µs 76.6 K 12.85 µs … 13.23 µs 13.14 µs 13.23 µs 13.23 µs -
join_param[n=1000,sep='-'] 131.75 µs 7.6 K 129.32 µs … 134.82 µs 132.23 µs 134.70 µs 134.76 µs -
join_param[n=1000,sep=':'] 135.62 µs 7.4 K 131.17 µs … 147.50 µs 136.68 µs 146.92 µs 147.21 µs -
group: strings
join-baseline ★ 376.07 ns 2.7 M 371.95 ns … 384.09 ns 378.96 ns 383.66 ns 383.87 ns baseline
join-basic 377.90 ns 2.6 M 365.89 ns … 382.65 ns 381.15 ns 382.55 ns 382.60 ns ≈ same
concat 10.62 µs 94.1 K 10.54 µs … 10.71 µs 10.65 µs 10.70 µs 10.71 µs 28.25× slower
💡 Tips
- Use
BenchContext.start()/end()para isolar a seção crítica e evitar ruído de setup. - Prefira
--profile smokedurante o desenvolvimento; troque para--profile thoroughantes de publicar números. - Para logs, use
--no-color.
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
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 pybenchx-1.2.0.tar.gz.
File metadata
- Download URL: pybenchx-1.2.0.tar.gz
- Upload date:
- Size: 128.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a1d1d6193fd7cc311cc6d904cb97e5ca47e55b698e65da471e430d18c6ffacc
|
|
| MD5 |
195d1732d4e8d013bc8792133f775b53
|
|
| BLAKE2b-256 |
355ec656ac9d91731619457c666bc51f07e590894448027226ab11175a7ad9d6
|
File details
Details for the file pybenchx-1.2.0-py3-none-any.whl.
File metadata
- Download URL: pybenchx-1.2.0-py3-none-any.whl
- Upload date:
- Size: 36.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
95956b69049e684b16e58225a0da04044e0d06ae91f1c3a2b1a5882b40c63008
|
|
| MD5 |
d7562cd6eea319606617e1116a582d20
|
|
| BLAKE2b-256 |
af6e427250c5afc92149d21b0a9167f6415820d82728d74f7972f4733d645583
|