From: <md...@us...> - 2007-11-06 21:33:39
|
Revision: 4136 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4136&view=rev Author: mdboom Date: 2007-11-06 13:33:37 -0800 (Tue, 06 Nov 2007) Log Message: ----------- Speed up pcolor_demo.py "benchmark" initialization by a factor of 2. Cache the automatically created path codes by their length. pcolor, quadmesh etc. create lots of polylines of the same length, and there is no need to create a new identical codes array each time. (Definite speed improvement, incredible memory improvement). Change the default behavior to create open paths (which don't result in a memory copy). Fix places that were relying on automatically-created closed paths to create closed paths themselves (and thus avoiding a copy). Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/collections.py branches/transforms/lib/matplotlib/contour.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-11-06 20:02:07 UTC (rev 4135) +++ branches/transforms/lib/matplotlib/axes.py 2007-11-06 21:33:37 UTC (rev 4136) @@ -4680,9 +4680,10 @@ xy = npy.concatenate((X1[:,newaxis], Y1[:,newaxis], X2[:,newaxis], Y2[:,newaxis], X3[:,newaxis], Y3[:,newaxis], - X4[:,newaxis], Y4[:,newaxis]), + X4[:,newaxis], Y4[:,newaxis], + X1[:,newaxis], Y1[:,newaxis]), axis=1) - verts = xy.reshape((npoly, 4, 2)) + verts = xy.reshape((npoly, 5, 2)) #verts = zip(zip(X1,Y1),zip(X2,Y2),zip(X3,Y3),zip(X4,Y4)) @@ -4696,6 +4697,7 @@ kwargs.setdefault('edgecolors', edgecolors) kwargs.setdefault('antialiaseds', (0,)) kwargs.setdefault('linewidths', (0.25,)) + kwargs['closed'] = False collection = mcoll.PolyCollection(verts, **kwargs) Modified: branches/transforms/lib/matplotlib/collections.py =================================================================== --- branches/transforms/lib/matplotlib/collections.py 2007-11-06 20:02:07 UTC (rev 4135) +++ branches/transforms/lib/matplotlib/collections.py 2007-11-06 21:33:37 UTC (rev 4136) @@ -390,14 +390,15 @@ paths = [] # We could let the Path constructor generate the codes for us, # but this is faster, since we know they'll always be the same - codes = npy.array([Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO]) + codes = npy.array([Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]) for m in xrange(meshHeight): for n in xrange(meshWidth): paths.append(Path( [c[m , n], c[m , n+1], c[m+1, n+1], - c[m+1, n]], + c[m+1, n], + c[m , n]], codes)) self._paths = paths @@ -424,13 +425,14 @@ %(Collection)s """ + self.closed = kwargs.pop("closed", True) Collection.__init__(self,**kwargs) self.set_verts(verts) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def set_verts(self, verts): '''This allows one to delay initialization of the vertices.''' - self._paths = [mpath.Path(v, closed=True) for v in verts] + self._paths = [mpath.Path(v, closed=self.closed) for v in verts] def get_paths(self): return self._paths @@ -611,7 +613,7 @@ segments = [npy.asarray(seg, npy.float_) for seg in segments] if self._uniform_offsets is not None: segments = self._add_offsets(segments) - self._paths = [mpath.Path(seg, closed=False) for seg in segments] + self._paths = [mpath.Path(seg) for seg in segments] set_verts = set_segments # for compatibility with PolyCollection Modified: branches/transforms/lib/matplotlib/contour.py =================================================================== --- branches/transforms/lib/matplotlib/contour.py 2007-11-06 20:02:07 UTC (rev 4135) +++ branches/transforms/lib/matplotlib/contour.py 2007-11-06 21:33:37 UTC (rev 4136) @@ -360,9 +360,9 @@ if inline: new = self.break_linecontour(linecontour, rotation, lw, ind) if len(new[0]): - paths[segNum] = path.Path(new[0], closed=False) + paths[segNum] = path.Path(new[0]) if len(new[1]): - additions.append(path.Path(new[1], closed=False)) + additions.append(path.Path(new[1])) paths.extend(additions) Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-11-06 20:02:07 UTC (rev 4135) +++ branches/transforms/lib/matplotlib/lines.py 2007-11-06 21:33:37 UTC (rev 4136) @@ -429,7 +429,7 @@ self._y = self._xy[:, 1] # just a view # Masked arrays are now handled by the Path class itself - self._path = Path(self._xy, closed=False) + self._path = Path(self._xy) self._transformed_path = TransformedPath(self._path, self.get_transform()) @@ -683,7 +683,7 @@ steps[0::2, 0], steps[1::2, 0] = vertices[:, 0], vertices[:-1, 0] steps[0::2, 1], steps[1:-1:2, 1] = vertices[:, 1], vertices[1:, 1] - path = Path(steps, closed=False) + path = Path(steps) self._draw_solid(renderer, gc, path, trans) @@ -694,7 +694,7 @@ steps[::2, 0], steps[1:-1:2, 0] = vertices[:, 0], vertices[1:, 0] steps[0::2, 1], steps[1::2, 1] = vertices[:, 1], vertices[:-1, 1] - path = Path(steps, closed=False) + path = Path(steps) self._draw_solid(renderer, gc, path, trans) @@ -708,7 +708,7 @@ steps[-1, 0] = vertices[-1, 0] steps[0::2, 1], steps[1::2, 1] = vertices[:, 1], vertices[:, 1] - path = Path(steps, closed=False) + path = Path(steps) self._draw_solid(renderer, gc, path, trans) @@ -756,7 +756,7 @@ rgbFace) - _triangle_path = Path([[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0]]) + _triangle_path = Path([[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]]) def _draw_triangle_up(self, renderer, gc, path, path_trans): offset = 0.5*renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset, offset) @@ -838,7 +838,7 @@ path, path_trans, rgbFace) - _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]], closed=False) + _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]]) def _draw_vline(self, renderer, gc, path, path_trans): offset = 0.5*renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset) @@ -853,7 +853,7 @@ path, path_trans) - _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]], closed=False) + _tickhoriz_path = Path([[0.0, 0.5], [1.0, 0.5]]) def _draw_tickleft(self, renderer, gc, path, path_trans): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(-offset, 1.0) @@ -868,7 +868,7 @@ path, path_trans) - _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]], closed=False) + _tickvert_path = Path([[-0.5, 0.0], [-0.5, 1.0]]) def _draw_tickup(self, renderer, gc, path, path_trans): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, offset) Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-11-06 20:02:07 UTC (rev 4135) +++ branches/transforms/lib/matplotlib/patches.py 2007-11-06 21:33:37 UTC (rev 4136) @@ -578,7 +578,7 @@ [ 0.0, 0.1 ], [ 0.0, -0.1], [ 0.8, -0.1 ], [ 0.8, -0.3], [ 1.0, 0.0 ], [ 0.8, 0.3], - [ 0.8, 0.1 ] ] ) + [ 0.8, 0.1 ], [ 0.0, 0.1] ] ) def __init__( self, x, y, dx, dy, width=1.0, **kwargs ): """Draws an arrow, starting at (x,y), direction and length @@ -727,8 +727,8 @@ xc1, yc1, xc2, yc2 = self.getpoints(x1, y1, xm, ym, k1) xd1, yd1, xd2, yd2 = self.getpoints(x1, y1, xm, ym, k2) - xs = self.convert_xunits([xb1, xb2, xc2, xd2, x1, xd1, xc1]) - ys = self.convert_yunits([yb1, yb2, yc2, yd2, y1, yd1, yc1]) + xs = self.convert_xunits([xb1, xb2, xc2, xd2, x1, xd1, xc1, xb1]) + ys = self.convert_yunits([yb1, yb2, yc2, yd2, y1, yd1, yc1, yb1]) return Path(zip(xs, ys)) Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-11-06 20:02:07 UTC (rev 4135) +++ branches/transforms/lib/matplotlib/path.py 2007-11-06 21:33:37 UTC (rev 4136) @@ -5,6 +5,7 @@ """ import math +from weakref import WeakValueDictionary import numpy as npy from matplotlib.numerix import npyma as ma @@ -65,8 +66,11 @@ NUM_VERTICES = [1, 1, 1, 2, 3, 1] code_type = npy.uint8 + + _open_codes_cache = WeakValueDictionary() + _closed_codes_cache = WeakValueDictionary() - def __init__(self, vertices, codes=None, closed=True): + def __init__(self, vertices, codes=None, closed=False): """ Create a new path with the given vertices and codes. @@ -88,25 +92,37 @@ resulting path will be compressed, with MOVETO codes inserted in the correct places to jump over the masked regions. """ - mask = ma.nomask if ma.isMaskedArray(vertices): mask = ma.getmask(vertices) else: vertices = npy.asarray(vertices, npy.float_) - + mask = ma.nomask + if codes is None: - if len(vertices) == 0: - codes = npy.zeros((0, ), self.code_type) - elif closed: - codes = self.LINETO * npy.ones( - vertices.shape[0] + 1, self.code_type) - codes[0] = self.MOVETO - codes[-1] = self.CLOSEPOLY + if closed: + # MGDTODO: Remove me once efficiency concerns are + # taken care of. + warnings.warn(""" +EFFICIENCY CONCERN: Having the Path constructor create a closed +polyline automatically is not always the most efficient way to do +things, since it causes a memory copy of the vertices array. If the +caller can easily close the polygon itself it should do so. +""") + codes = self._closed_codes_cache.get(len(vertices)) + if codes is None: + codes = self.LINETO * npy.ones( + vertices.shape[0] + 1, self.code_type) + codes[0] = self.MOVETO + codes[-1] = self.CLOSEPOLY + self._closed_codes_cache[len(vertices)] = codes vertices = npy.concatenate((vertices, [vertices[0]])) - else: - codes = self.LINETO * npy.ones( - vertices.shape[0], self.code_type) - codes[0] = self.MOVETO + else: + codes = self._open_codes_cache.get(len(vertices)) + if codes is None: + codes = self.LINETO * npy.ones( + vertices.shape[0], self.code_type) + codes[0] = self.MOVETO + self._open_codes_cache[len(vertices)] = codes else: codes = npy.asarray(codes, self.code_type) assert codes.ndim == 1 @@ -222,7 +238,7 @@ """ if cls._unit_rectangle is None: cls._unit_rectangle = \ - Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]) + Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]) return cls._unit_rectangle unit_rectangle = classmethod(unit_rectangle) @@ -308,7 +324,7 @@ [-1.0, 0.0]], npy.float_) - codes = cls.CURVE4 * npy.ones((len(vertices))) + codes = cls.CURVE4 * npy.ones(14) codes[0] = cls.MOVETO codes[-1] = cls.CLOSEPOLY This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |