[Zope-dev] Re: hasattr implementation for Zope?
Tim Peters
tim.peters at gmail.com
Sat May 28 12:04:35 EDT 2005
[Tim Peters]
>> def lookup1(arg, _marker=object()):
>> return _marker
>> ...
>> _marker = object()
>> def lookup3(arg):
>> return _marker
>> ...
>>lookup1 0.427597
>>lookup3 0.404399
[Dieter Maurer]
> Do you understand why "lookup3" is faster than "lookup1"?
>
> I had the "impression" that access to the function's local namespace
> should be faster than any other variable access.
It is, although module-global lookup is often much faster now than
Python old-timers "know" it is <wink>: the critical successful path
thru the dict lookup code for a dict keyed by interned strings is, in
the absence of collisions, _almost_ as lean as an indexed array access
now. The primary difference in speed remaining is that the dict
lookup endures an extra C-level function call.
I explained a relevant difference last time: lookup1 has the
additional expense of initializing an additional local variable.
That's what a default argument becomes, and nothing happens for free.
Let's time this effect "in isolation":
"""
from itertools import repeat
from time import clock as now
def with(default=5):
pass
def without():
pass
for dummy in range(3):
for f in with, without:
start = now()
for dummy in repeat(None, 1000000):
f()
finish = now()
print "%-8s %.6g" % (f.__name__, finish - start)
"""
and typical output:
with 0.374435
without 0.338555
with 0.370469
without 0.339791
with 0.372165
without 0.337355
So adding a default argument indeed has a measurable cost (although
~0.03 seconds across a million calls is nothing to fret about!). Note
that the difference between lookup1 and lookup3 was less than that on
this box, reflecting that the lookup _inside_ lookup1 goes faster than
the lookup inside lookup3, partly cancelling lookup1's higher call
overhead; the difference in call overhead is greater than the
difference in lookup speed, so lookup1 ends up a net loser.
More information about the Zope-Dev
mailing list