Python performance (Was: Re: [MERGE] Use slots consistently in...)
Jan Hudec
bulb at ucw.cz
Fri Jun 2 09:12:27 BST 2006
On Fri, Jun 02, 2006 at 11:54:43 +1000, Andrew Bennetts wrote:
> Some general comments on Python performance... mostly trivia, but perhaps of
> interest. :)
>
> On Thu, Jun 01, 2006 at 09:01:37PM +0200, Jan Hudec wrote:
> [...]
> >
> > def use_getattr(i):
> > return getattr(i, 'notattr', None)
> >
> > The turns out to be quite dissatisfactory. That surprises me a little bit,
> > but I guess that it can be explained by the need to lookup getattr by name
> > and the fact the attribute name is not interned.
>
> The string is in fact interned. CPython interns string literals of valid
> identifiers.
>
> If you made getattr a local, either using the make_constants decorator from
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/277940 or just defining
> use_getattr as "def use_getattr(i, getattr=getattr): ...", you'd save the look
> up of the getattr function by name. That would save two dictionary lookups; one
> miss in the module globals dict, and then the hit in the builtins dict. Or at
> least that's the theory; for some reason doing this gives slightly worse results
> for me. I'm not sure why; I think it may be because one-argument functions are
> special-cased, and this trick breaks that.
>
> The other obvious cost here is the function call. Although getattr is
> implemented in C, calling it still requires building a tuple of the args. (Some
> C functions that always take exactly one argument don't have this cost, but
> mostly they do).
Sounds interesting.
> > def use_classattr(i):
> > return i.classattr
> >
> > The classattr is a classattr in the base class. As you can see, it's the
> > fastest in all cases.
>
> classattr isn't a class attribute, though! Well, it is for cNone objects, but
> it's an *instance* attribute for cSlot and cDict objects.
>
> (perhaps that's intentionally what you're testing, but your benchmark doesn't
> make that clear...)
Yes. It was intentional. There is a function (in the serializer), that
needs to access an attribute of bunch of objects. But only some of those
objects have it. And I wanted to know which solution works fast both for
the objects that do and those that don't have it.
> > def use_property(i):
> > return i.propattr
> >
> > The call to lambda self: None seems to really hurt performance, so I'm going
> > to avoid this now and in future. Especially since when the instance does not
> > have __dict__, class attribute makes a good read-only attribute on the
> > instance.
>
> Python function calls are *really* slow compared to basically any other
> operation in python, due to the overhead of setting up and tearing down frames.
> (One of the nice things about generators is that iterating them doesn't have
> this cost; the frame is already set up, so re-entering the generator function is
> very fast).
Yes, someone already mentioned calls to python functions are damn slow.
Which is a pity, because it discourages abstractions.
> > def use_hasattr(i):
> > if hasattr(i, 'notattr'):
> > return i.notattr
> > else:
> > return None
> >
> > Although the documentation says it calls getattr and catches the exception,
> > it is quite significantly faster than try/catch in the bad case. I guess
> > given the explicit way python handles exceptions on the C level, they are
> > a lot easier to catch there.
>
> Yeah, but trading speed for correctness isn't such a great idea ;)
Sorry, but I did not get this comment. What is trading speed for correctness?
> [...]
> >
> > class cSlot(cNone):
> > kind = 'good'
> > __slots__ = ['classattr', 'notattr', 'propattr']
> > def __init__(self):
> > self.classattr = True
> > self.notattr = True
> > self.propattr = True
>
> I believe this should be:
>
> class cSlot(cNone):
> kind = 'good'
> __slots__ = ['notattr', 'propattr']
> def __init__(self):
> self.notattr = True
> self.propattr = True
Nope. I /always/ want to access instance attributes on the 'good classes' --
the not/class/prop only pertains to what it is on the /bad/ class. Because I am
comparing the case when the attribute is there with the case there isn't (and
either access throws AttributeError, or a default value is provided by the
class).
> Note that I'd expect this to be slightly slower at accessing classattr than:
>
> class cSlot(cNone):
> kind = 'good'
> classattr = None
> __slots__ = ['notattr', 'propattr']
> def __init__(self):
> self.notattr = True
> self.propattr = True
>
> Because generally attributes are first looked for on the instance, then on each
> class in the MRO in turn, until it is found, or all classes are exhausted.
But only really slightly -- I think python actually caches where to look for an attribute.
--
Jan 'Bulb' Hudec <bulb at ucw.cz>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20060602/2e7afb81/attachment.pgp
More information about the bazaar
mailing list