Hi Ben,
thank your for your comments. OK, here are revised patches. I see a hot spot
in artist.py where the getattr() calls are too expensive. Actually, those under
the callable() path.
793 class ArtistInspector:
817 def get_aliases(self):
818 """
819 Get a dict mapping *fullname* -> *alias* for each *alias* in
820 the :class:`~matplotlib.artist.ArtistInspector`.
821
822 Eg., for lines::
823
824 {'markerfacecolor': 'mfc',
825 'linewidth' : 'lw',
826 }
827
828 """
829 names = [name for name in dir(self.o) if
830 (name[:4] in ['set_', 'get_'])
831 and callable(getattr(self.o, name))]
832 aliases = {}
833 for name in names:
834 func = getattr(self.o, name)
835 if not self.is_alias(func):
836 continue
837 docstring = func.__doc__
838 fullname = docstring[10:]
839 aliases.setdefault(fullname[4:], {})[name[4:]] = None
840 return aliases
Another hot spot is setp() artist.py, actually its get_aliases() on line 817,
which again leads to getattr() and callable(). The problem is there are
millions of
their calls.
So, once again, my all my patches attached (figure.py.patch artist.py.patch
have changed).
Martin
Benjamin Root wrote:
>
> On Fri, Aug 9, 2013 at 9:04 AM, Martin Mokrejs <mmokr...@fold.natur.cuni.cz
> <mailto:mmokr...@fold.natur.cuni.cz>> wrote:
>
> Hi Phil,
>
> Phil Elson wrote:
> > Hi Martin,
> >
> > Thanks for this - we are really interested in speeding up the scatter
> and barchart plotting with large data sets. In fact, we've done some work
> (https://github.com/matplotlib/matplotlib/pull/2156) recently to make the
> situation better.
> >
> > I'd really like to review these changes (against matplotlib master),
> and the best possible solution to doing this is if you were to submit a pull
> request. If the changes you have made are logically seperable, then I'd
> encourage you to make a few PRs, but otherwise, a single PR with all of these
> changes would be great.
>
> I went through the changes there and they just cope with other pieces of
> matplotlib.
> My changes are general python improvements moving away from
> str.startswith()
> and using generators instead of for loops. Just apply the patches
> yourself and see.
> ;)
>
> >
> > Would you mind turning these patches into PR(s)?
> (https://github.com/matplotlib/matplotlib/compare/)
>
> Um, I don't know what to do on that page, sorry. I don't see how to
> upload my patch file or patched file
> to be compared with master. :(
>
> >
> > Thanks!
>
> I am sorry but I just don't have time to fiddle with github. It is just
> awkward. I even failed to download
> diffs of the changes from
> https://github.com/matplotlib/matplotlib/pull/2156/commits.
>
> I rather continue studying runsnake output. ;-)
>
> Martin
>
>
> A snippet from one of you patches:
>
> dsu = []
>
> - for a in self.patches:
> - dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
> + [ dsu.append( (x.get_zorder(), x, x.draw, [renderer])) for x in
> self.patches ]
>
> Yes, we certainly should use list-comprehensions here, but you are using it
> incorrectly. It should be:
>
> dsu = [(x.get_zorder(), x, x.draw, [renderer])) for x in self.patches ]
>
> And then, further down, do the following:
>
> dsu.extend((x.get_zorder(), x, x.draw, [renderer])) for x in self.lines)
>
> Note the generator form of the comprehension as opposed to the list
> comprehension form. List comprehensions should *always* be assigned to
> something. List comprehensions should only be for replacing the for-append
> idiom in python.
>
> Thank you though for pointing out parts of the code that can benefit from
> revisions. I certainly hope you can get this put together as a pull request
> on github so we can work to make this patch better!
>
> Ben Root
>
> P.S. - I <3 runsnakerun!
--- lib/matplotlib/axes.py.ori 2013-08-09 13:46:16.000000000 +0200
+++ lib/matplotlib/axes.py 2013-08-09 13:46:44.000000000 +0200
@@ -4424,7 +4424,7 @@
for handle in self._get_legend_handles(legend_handler_map):
label = handle.get_label()
#if (label is not None and label != '' and not label.startswith('_')):
- if label and not label.startswith('_'):
+ if label and label[0] != '_':
handles.append(handle)
labels.append(label)
@@ -8135,7 +8135,7 @@
patches = []
- if histtype.startswith('bar'):
+ if histtype[:3] == 'bar':
totwidth = np.diff(bins)
if rwidth is not None:
@@ -8183,7 +8183,7 @@
bottom[:] = m
boffset += dw
- elif histtype.startswith('step'):
+ elif histtype[:4] == 'step':
# these define the perimeter of the polygon
x = np.zeros( 4*len(bins)-3, np.float )
y = np.zeros( 4*len(bins)-3, np.float )
--- lib/matplotlib/artist.py.ori 2013-08-09 13:46:22.000000000 +0200
+++ lib/matplotlib/artist.py 2013-08-09 17:03:16.000000000 +0200
@@ -827,7 +827,7 @@
"""
names = [name for name in dir(self.o) if
- (name.startswith('set_') or name.startswith('get_'))
+ (name[:4] in ['set_', 'get_'])
and callable(getattr(self.o, name))]
aliases = {}
for name in names:
@@ -862,7 +862,7 @@
if docstring is None:
return 'unknown'
- if docstring.startswith('alias for '):
+ if docstring[:10] == 'alias for ':
return None
match = self._get_valid_values_regex.search(docstring)
@@ -878,7 +878,7 @@
setters = []
for name in dir(self.o):
- if not name.startswith('set_'):
+ if name[:4] != 'set_':
continue
o = getattr(self.o, name)
if not callable(o):
@@ -910,7 +910,7 @@
ds = o.__doc__
if ds is None:
return False
- return ds.startswith('alias for ')
+ return ds[:10] == 'alias for '
def aliased_name(self, s):
"""
--- lib/matplotlib/figure.py 2013-08-09 16:12:02.000000000 +0200
+++ /usr/lib64/python2.7/site-packages/matplotlib/figure.py 2013-08-09 16:02:46.000000000 +0200
@@ -945,16 +945,11 @@
if self.frameon: self.patch.draw(renderer)
# a list of (zorder, func_to_call, list_of_args)
- dsu = []
+ dsu = [(x.get_zorder(), x, x.draw, [renderer]) for x in self.patches ]
- for a in self.patches:
- dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
-
- for a in self.lines:
- dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
+ dsu.extend((x.get_zorder(), x, x.draw, [renderer]) for x in self.lines)
- for a in self.artists:
- dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
+ dsu.extend((x.get_zorder(), x, x.draw, [renderer]) for x in self.artists)
# override the renderer default if self.suppressComposite
# is not None
@@ -990,15 +985,12 @@
dsu.append((self.images[0].get_zorder(), self.images[0], draw_composite, []))
# render the axes
- for a in self.axes:
- dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
+ dsu.extend((x.get_zorder(), x, x.draw, [renderer]) for x in self.axes)
# render the figure text
- for a in self.texts:
- dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
+ dsu.extend((x.get_zorder(), x, x.draw, [renderer]) for x in self.texts)
- for a in self.legends:
- dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
+ dsu.extend((x.get_zorder(), x, x.draw, [renderer]) for x in self.legends)
dsu = [row for row in dsu if not row[1].get_animated()]
dsu.sort(key=itemgetter(0))
------------------------------------------------------------------------------
Get 100% visibility into Java/.NET code with AppDynamics Lite!
It's a free troubleshooting tool designed for production.
Get down to code-level detail for bottlenecks, with <2% overhead.
Download for free and get started troubleshooting in minutes.
http://pubads.g.doubleclick.net/gampad/clk?id=48897031&iu=/4140/ostg.clktrk
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users