|
| 1 | +"""Measure import time and peak Python allocation for reproducible fixtures. |
| 2 | +
|
| 3 | +Run from an installed checkout: python scripts/benchmark_matplotlib_import.py |
| 4 | +Timings are observations, not brittle pass/fail thresholds. Native renderer |
| 5 | +allocations outside Python are not included in tracemalloc's peak measurement. |
| 6 | +""" |
| 7 | + |
| 8 | +import argparse |
| 9 | +import json |
| 10 | +import time |
| 11 | +import tracemalloc |
| 12 | + |
| 13 | +import matplotlib |
| 14 | + |
| 15 | +matplotlib.use("Agg") |
| 16 | +import matplotlib.pyplot as plt |
| 17 | +import numpy as np |
| 18 | + |
| 19 | +from maxplotlib import Canvas |
| 20 | + |
| 21 | + |
| 22 | +def benchmark(points, panels): |
| 23 | + fig, axes = plt.subplots(panels, 1, figsize=(8, max(3, panels * 2))) |
| 24 | + x = np.linspace(0, 10, points) |
| 25 | + for index, ax in enumerate(np.atleast_1d(axes)): |
| 26 | + ax.plot(x, np.sin(x + index)) |
| 27 | + ax.scatter([], []) |
| 28 | + tracemalloc.start() |
| 29 | + started = time.perf_counter() |
| 30 | + canvas = Canvas.from_matplotlib(fig, strict=True) |
| 31 | + imported = time.perf_counter() |
| 32 | + _, peak = tracemalloc.get_traced_memory() |
| 33 | + tracemalloc.stop() |
| 34 | + result, _ = canvas.render() |
| 35 | + result.canvas.draw() |
| 36 | + finished = time.perf_counter() |
| 37 | + stats = dict( |
| 38 | + matplotlib=matplotlib.__version__, |
| 39 | + points_per_panel=points, |
| 40 | + panels=panels, |
| 41 | + import_seconds=imported - started, |
| 42 | + render_seconds=finished - imported, |
| 43 | + import_peak_python_mib=peak / 1024**2, |
| 44 | + entries=sum(len(plot.line_data) for _, _, plot in canvas.iter_subplots()), |
| 45 | + ) |
| 46 | + plt.close(fig) |
| 47 | + plt.close(result) |
| 48 | + return stats |
| 49 | + |
| 50 | + |
| 51 | +if __name__ == "__main__": |
| 52 | + parser = argparse.ArgumentParser(description=__doc__) |
| 53 | + parser.add_argument("--points", type=int, default=100_000) |
| 54 | + parser.add_argument("--panels", type=int, default=4) |
| 55 | + arguments = parser.parse_args() |
| 56 | + if arguments.points < 1 or arguments.panels < 1: |
| 57 | + parser.error("points and panels must be positive") |
| 58 | + print(json.dumps(benchmark(arguments.points, arguments.panels), indent=2)) |
0 commit comments