From: Patrick Maupin on
On Mar 17, 2:55 pm, Terry Reedy <tjre...(a)udel.edu> wrote:
> On 3/17/2010 1:35 AM, Patrick Maupin wrote:
>
> >>>> def a(s, count, lenfunc):
> > ...     for i in xrange(count):
> > ...        z = lenfunc(s)
> > ...
> >>>> >>>  a('abcdef', 100000000, len)
> >>>> >>>  a('abcdef', 100000000, str.__len__)
> > Running cPython 2.6 on my machine, len() runs about 3 times faster
> > than str.__len__().  The overhead of checking that an object is usable
> > with a particular class method far outweighs the cost of creating the
> > bound method!
>
> Wow, this so surprised me, that I had to try it with 3.1 (on winxp), and
> got a similar result (about 2.6x longer with str.__len__). This is a
> real lesson in measure, don't guess, and how premature 'optimization'
> may be a pessimization. Thanks.
>
> Terry Jan Reedy

Actually, I think I overstated my case -- there is some special logic
for len and built-in objects, I think. I can see the same thing with
normal attributes on subclasses of object(), but not nearly as
dramatic. In any case, your conclusion about this being a lesson in
"measure, don't guess" holds, with the additional caveat that, if it
matters, you need to somehow do some additional measurements to make
sure you are measuring what you think you are measuring!

Pat
From: Joaquin Abian on
On Mar 17, 3:43 pm, Patrick Maupin <pmau...(a)gmail.com> wrote:
> On Mar 17, 4:12 am, Bruno Desthuilliers <bruno.
>
> 42.desthuilli...(a)websiteburo.invalid> wrote:
> > Patrick Maupin a écrit :
>
> > > On Mar 16, 1:59 pm, Jason Tackaberry <t...(a)urandom.ca> wrote:
> > >> Why not create the bound methods at instantiation time, rather than
> > >> using the descriptor protocol which has the overhead of creating a new
> > >> bound method each time the method attribute is accessed?
>
> > > Well, for one thing, Python classes are open.  They can be added to at
> > > any time.  For another thing, you might not ever use most of the
> > > methods of an instance, so it would be a huge waste to create those.
>
> > A possible optimization would be a simple memoization on first access.
>
> I do agree that memoization on access is a good pattern, and I use it
> frequently.  I don't know if I would want the interpreter
> automagically doing that for everything, though -- it would require
> some thought to figure out what the overhead cost is for the things
> that are only used once.
>
> Usually, I will have a slight naming difference for the things I want
> memoized, to get the memoization code to run.  For example, if you add
> an underbar in front of everything you want memoized:
>
> class foo(object):
>
>     def _bar(self):
>         pass
>
>     def __getattr__(self, aname):
>         if aname.startswith('_'):
>             raise AttributeError
>         value = getattr(self, '_' + aname)
>         self.aname = value
>         return value
>
> obj = foo()
>
> So then the first time you look up obj.bar, it builds the bound
> method, and on subsequent accesses it just returns the previously
> bound method.
>
> Regards,
> Pat

Patrick, I was trying to understand the way your code was working but
I thing I'm not getting it.

I tested:

from time import time

class foo1(object):
def _bar(self):
pass
def __getattr__(self, name):
value = getattr(self, '_' + name)
self.name = value
return value

class foo2(object):
def bar(self):
pass

def a(klass, count):
ins = klass()
for i in xrange(count):
z = ins.bar()

t0 = time()
a(foo1, 10000000)
t1 = time()
a(foo2, 10000000)
t2 = time()

print t1-t0 #75 sec
print t2-t1 #11 sec

foo1 is a lot slower than foo2. I understood that memoization should
optimize atribute calls. Maybe I am putting my foot in my mouth...

Thanks
JA
From: Patrick Maupin on
On Mar 17, 5:34 pm, Joaquin Abian <gatoyga...(a)gmail.com> wrote:
> On Mar 17, 3:43 pm, Patrick Maupin <pmau...(a)gmail.com> wrote:
>
>
>
> > On Mar 17, 4:12 am, Bruno Desthuilliers <bruno.
>
> > 42.desthuilli...(a)websiteburo.invalid> wrote:
> > > Patrick Maupin a écrit :
>
> > > > On Mar 16, 1:59 pm, Jason Tackaberry <t...(a)urandom.ca> wrote:
> > > >> Why not create the bound methods at instantiation time, rather than
> > > >> using the descriptor protocol which has the overhead of creating a new
> > > >> bound method each time the method attribute is accessed?
>
> > > > Well, for one thing, Python classes are open.  They can be added to at
> > > > any time.  For another thing, you might not ever use most of the
> > > > methods of an instance, so it would be a huge waste to create those..
>
> > > A possible optimization would be a simple memoization on first access..
>
> > I do agree that memoization on access is a good pattern, and I use it
> > frequently.  I don't know if I would want the interpreter
> > automagically doing that for everything, though -- it would require
> > some thought to figure out what the overhead cost is for the things
> > that are only used once.
>
> > Usually, I will have a slight naming difference for the things I want
> > memoized, to get the memoization code to run.  For example, if you add
> > an underbar in front of everything you want memoized:
>
> > class foo(object):
>
> >     def _bar(self):
> >         pass
>
> >     def __getattr__(self, aname):
> >         if aname.startswith('_'):
> >             raise AttributeError
> >         value = getattr(self, '_' + aname)
> >         self.aname = value
> >         return value
>
> > obj = foo()
>
> > So then the first time you look up obj.bar, it builds the bound
> > method, and on subsequent accesses it just returns the previously
> > bound method.
>
> > Regards,
> > Pat
>
> Patrick, I was trying to understand the way your code was working but
> I thing I'm not getting it.
>
> I tested:
>
> from time import time
>
> class foo1(object):
>     def _bar(self):
>         pass
>     def __getattr__(self, name):
>         value = getattr(self, '_' + name)
>         self.name = value
>         return value
>
> class foo2(object):
>     def bar(self):
>         pass
>
> def a(klass, count):
>     ins = klass()
>     for i in xrange(count):
>         z = ins.bar()
>
> t0 = time()
> a(foo1,  10000000)
> t1 = time()
> a(foo2, 10000000)
> t2 = time()
>
> print t1-t0   #75 sec
> print t2-t1   #11 sec
>
> foo1 is a lot slower than foo2. I understood that memoization should
> optimize atribute calls. Maybe I am putting my foot in my mouth...
>
> Thanks
> JA

I don't think you are putting your foot in your mouth. I always have
to test to remember what works faster and what doesn't. Usually when
I memoize as I showed, it is not a simple attribute lookup, but
something that takes more work to create. As I stated in my response
to Terry, I overstated my case earlier, because of some optimizations
in len(), I think. Nonetheless, (at least on Python 2.6) I think the
advice I gave to the OP holds. One difference is that you are doing
an attribute lookup in your inner loop. I do find that performance
hit surprising, but to compare with what the OP is describing, you
either need to look up an unbound function in a dict and call it with
a parameter, or look up a bound method in a dict and call it without
the parameter. Since the dict lookup doesn't usually do anything
fancy and unexpected like attribute lookup, we pull the dict lookup
out of the equation and out of the inner loop, and just do the
comparison like this:

>>> class foo(object):
.... def bar(self):
.... pass
....
>>> x = foo()
>>>
>>> def a(func, count):
.... for i in xrange(count):
.... z=func()
....
>>> def b(func, param, count):
.... for i in xrange(count):
.... z=func(param)
....
>>>
>>> a(x.bar, 100000000) # 13 seconds
>>> b(foo.bar, x, 100000000) # 18 seconds

Regards,
Pat
From: Joaquin Abian on
On Mar 18, 12:11 am, Patrick Maupin <pmau...(a)gmail.com> wrote:
> On Mar 17, 5:34 pm, Joaquin Abian <gatoyga...(a)gmail.com> wrote:
>
>
>
> > On Mar 17, 3:43 pm, Patrick Maupin <pmau...(a)gmail.com> wrote:
>
> > > On Mar 17, 4:12 am, Bruno Desthuilliers <bruno.
>
> > > 42.desthuilli...(a)websiteburo.invalid> wrote:
> > > > Patrick Maupin a écrit :
>
> > > > > On Mar 16, 1:59 pm, Jason Tackaberry <t...(a)urandom.ca> wrote:
> > > > >> Why not create the bound methods at instantiation time, rather than
> > > > >> using the descriptor protocol which has the overhead of creating a new
> > > > >> bound method each time the method attribute is accessed?
>
> > > > > Well, for one thing, Python classes are open.  They can be added to at
> > > > > any time.  For another thing, you might not ever use most of the
> > > > > methods of an instance, so it would be a huge waste to create those.
>
> > > > A possible optimization would be a simple memoization on first access.
>
> > > I do agree that memoization on access is a good pattern, and I use it
> > > frequently.  I don't know if I would want the interpreter
> > > automagically doing that for everything, though -- it would require
> > > some thought to figure out what the overhead cost is for the things
> > > that are only used once.
>
> > > Usually, I will have a slight naming difference for the things I want
> > > memoized, to get the memoization code to run.  For example, if you add
> > > an underbar in front of everything you want memoized:
>
> > > class foo(object):
>
> > >     def _bar(self):
> > >         pass
>
> > >     def __getattr__(self, aname):
> > >         if aname.startswith('_'):
> > >             raise AttributeError
> > >         value = getattr(self, '_' + aname)
> > >         self.aname = value
> > >         return value
>
> > > obj = foo()
>
> > > So then the first time you look up obj.bar, it builds the bound
> > > method, and on subsequent accesses it just returns the previously
> > > bound method.
>
> > > Regards,
> > > Pat
>
> > Patrick, I was trying to understand the way your code was working but
> > I thing I'm not getting it.
>
> > I tested:
>
> > from time import time
>
> > class foo1(object):
> >     def _bar(self):
> >         pass
> >     def __getattr__(self, name):
> >         value = getattr(self, '_' + name)
> >         self.name = value
> >         return value
>
> > class foo2(object):
> >     def bar(self):
> >         pass
>
> > def a(klass, count):
> >     ins = klass()
> >     for i in xrange(count):
> >         z = ins.bar()
>
> > t0 = time()
> > a(foo1,  10000000)
> > t1 = time()
> > a(foo2, 10000000)
> > t2 = time()
>
> > print t1-t0   #75 sec
> > print t2-t1   #11 sec
>
> > foo1 is a lot slower than foo2. I understood that memoization should
> > optimize atribute calls. Maybe I am putting my foot in my mouth...
>
> > Thanks
> > JA
>
> I don't think you are putting your foot in your mouth.  I always have
> to test to remember what works faster and what doesn't.  Usually when
> I memoize as I showed, it is not a simple attribute lookup, but
> something that takes more work to create.  As I stated in my response
> to Terry, I overstated my case earlier, because of some optimizations
> in len(), I think.  Nonetheless, (at least on Python 2.6) I think the
> advice I gave to the OP holds.  One difference is that you are doing
> an attribute lookup in your inner loop.  I do find that performance
> hit surprising, but to compare with what the OP is describing, you
> either need to look up an unbound function in a dict and call it with
> a parameter, or look up a bound method in a dict and call it without
> the parameter.  Since the dict lookup doesn't usually do anything
> fancy and unexpected like attribute lookup, we pull the dict lookup
> out of the equation and out of the inner loop, and just do the
> comparison like this:
>
> >>> class foo(object):
>
> ...     def bar(self):
> ...         pass
> ...>>> x = foo()
>
> >>> def a(func, count):
>
> ...     for i in xrange(count):
> ...         z=func()
> ...>>> def b(func, param, count):
>
> ...     for i in xrange(count):
> ...         z=func(param)
> ...
>
>
>
> >>> a(x.bar,      100000000)  # 13 seconds
> >>> b(foo.bar, x, 100000000)  # 18 seconds
>
> Regards,
> Pat

OK, Thanks. Need to play a little bit with it.
Cheers
JA
From: Gregory Ewing on
Patrick Maupin wrote:

> Actually, I think I overstated my case -- there is some special logic
> for len and built-in objects, I think.

Yes, len() invokes the C-level sq_len slot of the type object,
which for built-in types points directly to the C function
implementing the len() operation for that type. So len() on
a string doesn't involve looking up a __len__ attribute or
creating a bound method at all.

The only reason a __len__ attribute exists at all for
built-in types is to give Python code the illusion that
C-coded and Python-coded classes work the same way. Most
of the time it's never used.

If you try the same test using a method that doesn't have
a corresponding type slot (basically anything without a
double_underscore name) you will probably see a small
improvement.

--
Greg