From 6afd28cbdaae0e14c382463fc95b1f4342037717 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 26 Jan 2018 00:05:48 -0800 Subject: [PATCH] Don't call np.identity() in transforms. Speeds up setting up of subplots by ~3% and their drawing by ~2% at a reasonably limited cost in legibility. The difference is actually not easy to observe because variation in timings are much greater than 3% from run to run. In a call like `subplots(10, 10)`, just setting up the figure calls `np.identity(3)` 1801 times and actually drawing the figure another 1705 times (measured by manually instrumenting the calls to identity() in the old version. At the same time, ``` %timeit np.identity(3) ``` and ``` %%timeit t = np.identity(3) t.copy() ``` show that on my machine, a call to np.identity(3) is approximately 16us slower than copying a preexisting matrix; multiplying this by 1801 shows that the calls to identity correspond to an excess time of ~27ms. Finally, setting up the axes and drawing it can be timed using ``` from time import perf_counter import matplotlib; matplotlib.use("agg") from matplotlib import pyplot as plt N = 10 # First draw always seems slower, so skip it. figure = plt.figure() figure.subplots(N, N) figure.canvas.draw() plt.close("all") for _ in range(5): figure = plt.figure() start = perf_counter() figure.subplots(N, N) print("{: 4}".format(int(1000 * (perf_counter() - start))), end=" ") start = perf_counter() figure.canvas.draw() print("{: 4}".format(int(1000 * (perf_counter() - start)))) plt.close("all") ``` which shows that (on my machine) setting up the subplots takes ~850ms and drawing them ~1300ms (but again, with a lot of jitter). Hence, the gain from the patch should be ~3% for the setup and ~2% for the draw. --- lib/matplotlib/transforms.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 464105ecf3dc..15c457171295 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1928,7 +1928,8 @@ def __init__(self, matrix=None, **kwargs): """ Affine2DBase.__init__(self, **kwargs) if matrix is None: - matrix = np.identity(3) + # A bit faster than np.identity(3). + matrix = IdentityTransform._mtx.copy() self._mtx = matrix self._invalid = 0 @@ -1999,13 +2000,14 @@ def identity(): Unless this transform will be mutated later on, consider using the faster :class:`IdentityTransform` class instead. """ - return Affine2D(np.identity(3)) + return Affine2D() def clear(self): """ Reset the underlying matrix to the identity transform. """ - self._mtx = np.identity(3) + # A bit faster than np.identity(3). + self._mtx = IdentityTransform._mtx.copy() self.invalidate() return self