Reproducible micro-benchmarks for pickbuckets fit, transform, and the
pure-Python runtime apply path.
# Full suite (small, medium, large)
python benchmarks/run_benchmarks.py
# Quick smoke run (small + medium only)
python benchmarks/run_benchmarks.py --quick
# Machine-readable output for before/after comparisons
python benchmarks/run_benchmarks.py --json > before.json
# ...make a change...
python benchmarks/run_benchmarks.py --json > after.jsonequal_width.fit/equal_width.transformequal_frequency.fitruntime.apply_rule— the dependency-free apply pathstreaming.partial_fit[bounded_memory]— the streaming sketch, included as a memory check: its bounded state keeps peak memory roughly flat as the input growsrare_category.fit[high_cardinality]— categorical grouping with many keyspandas.transform/polars.transform— adapter paths (skipped if the library is not installed, so the core path benchmarks with no heavy deps)
Each row reports both speed (seconds, rows/s) and memory (peak KB,
B/row).
Peak heap memory is measured with the standard-library tracemalloc module — no
psutil or RSS sampling, so it is deterministic and cross-platform. Memory is
measured in a separate pass from timing (so it never pollutes the timings), and
only allocations inside the fit/transform are counted; input data allocated
beforehand is excluded.
This makes large-input memory behaviour visible and regression-testable:
- Materializing paths (
runtime.apply_rule, adapter transforms) allocate an output per row, soB/rowshould stay roughly constant as size grows. streaming.partial_fitkeeps a bounded sketch, so its peak memory stays roughly flat even as the input grows by orders of magnitude — verified bytests/test_benchmarks.py.
Pass --no-memory to time only and skip the memory pass.
Inputs are generated from a fixed seed (SEED = 1234), so two runs on the same
machine are directly comparable. Each benchmark is timed --repeats times
(default 3) and the best (minimum) wall-clock time is reported, which is the
most stable estimator for short CPU-bound work.
run() returns a list of BenchmarkResult dataclasses, and --json emits the
same data, so a PR can diff before.json against after.json. Absolute numbers
depend on the machine; compare relative throughput (rows/s) on the same host.
- The pure-Python
apply_rulepath trades raw speed for zero dependencies and portability. The pandas/Polars adapters are faster on large frames because they vectorize; the runtime path wins on startup cost and deployment size. equal_frequency.fitsorts its input, so it isO(n log n);equal_width.fitonly scans for min/max and isO(n).- High-cardinality categorical fitting is dominated by the frequency count, which
is
O(n)in the number of rows plusO(k log k)in the number of distinct categories.