Performance¶
Benchmarks¶
All numbers: Python 3.12, matplotlib Agg backend, median of 5 warm runs.
8-parameter, 50 000-sample chain¶
| Path | Wall time | vs. corner |
|---|---|---|
corner() |
3.20 s | 1.0× |
quick_corner() |
0.46 s | 7.0× faster |
Scaling with dimension (50 000 samples)¶
N_params |
corner |
quick_corner |
|---|---|---|
| 4 | ~0.9 s | 0.12 s |
| 8 | ~3.2 s | 0.46 s |
| 12 | ~8.5 s | 1.4 s |
At low dimensions the bottleneck is matplotlib axis setup; at higher
dimensions it shifts to 2-D KDE evaluations. quick_corner addresses both.
Where the time goes¶
For the default corner() path on an 8-param × 50 k-sample chain, cProfile
reports:
- ~70 % in KDExpress FFT-KDE evaluations + bandwidth estimation.
- ~15 % in matplotlib contour / line construction.
- ~10 % in matplotlib axis / tick / transform setup.
- ~5 % in cornetto glue (parsing, stat evaluation, titles).
quick_corner eliminates the first bucket entirely, shrinks the second with
lower bins and fewer contour levels, and shrinks the third by only creating
visible axes and sharing x per column.
Speed tips¶
For corner()¶
- Thin the chain.
subsample=10_000is indistinguishable from the full posterior in most cases and roughly halves the KDE cost. - Use
params=[...]early. Cornetto only KDEs what it draws. - Smaller grid.
n_grid=64is about 2.5× faster than the defaultn_grid=128and barely affects the contour shapes. - Reuse the object.
Cornetto.plot()caches KDEs and statistics - call.plot()multiple times with different styling kwargs for free.
For quick_corner()¶
- Fewer bins.
bins=20produces a blocky but fast preview. - One contour level.
sigmas=(2,)skips the 1σ band. - Smaller figure.
fig_size_per_dim=1.6trims matplotlib setup time.
Reproducing the numbers¶
Run this notebook to reproduce the benchmark on your machine.
# %%
import time
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from cornetto import corner, quick_corner
rng = np.random.default_rng(0)
data = {f"p{i}": rng.normal(i, 1 + 0.1 * i, 50_000) for i in range(8)}
# %% warm-ups (first call JIT-compiles KDExpress)
corner({"a": data["p0"][:1000], "b": data["p1"][:1000]})
plt.close("all")
quick_corner(data)
plt.close("all")
# %%
for fn in (corner, quick_corner):
times = []
for _ in range(5):
t0 = time.perf_counter()
fig, _ = fn(data)
times.append(time.perf_counter() - t0)
plt.close(fig)
print(f"{fn.__name__:13s} min={min(times):.3f}s median={sorted(times)[2]:.3f}s")