dynamism
Steven Shaw
steven_shaw at adc.com
Wed Sep 11 20:33:36 EDT 2002
Duncan Booth <duncan at NOSPAMrcp.co.uk> wrote in message news:<Xns9286606D44C35duncanrcpcouk at 127.0.0.1>...
> Duncan Booth <duncan at NOSPAMrcp.co.uk> wrote in
> news:Xns92865CE1C916Aduncanrcpcouk at 127.0.0.1:
> > The code below gives these timings on my machine:
> > 3.84s: 1000000 x New()
> > 3.89s: 1000000 x New2()
> > 2.39s: 1000000 x Old()
> > 5.19s: 10000 x attributes(<class '__main__.New'>)
> > 5.81s: 10000 x attributes(<class '__main__.New2'>)
> > 4.17s: 10000 x attributes(__main__.Old)
>
> Sorry about the bad form of following up to my own post, but I thought I
> had better add that the above timings are on Python 2.2.1 and I forgot to
> specify -O.
>
> The optimised times for 2.2.1 are:
> 3.77s: 1000000 x New()
> 3.81s: 1000000 x New2()
> 2.33s: 1000000 x Old()
> 4.94s: 10000 x attributes(<class '__main__.New'>)
> 5.56s: 10000 x attributes(<class '__main__.New2'>)
> 3.92s: 10000 x attributes(__main__.Old)
>
> Using the latest CVS build I get rather different timings:
>
> 0.94s: 1000000 x New()
> 1.05s: 1000000 x New2()
> 1.39s: 1000000 x Old()
> 3.52s: 10000 x attributes(<class '__main__.New'>)
> 4.36s: 10000 x attributes(<class '__main__.New2'>)
> 3.05s: 10000 x attributes(__main__.Old)
>
> So it appears that creating object-derived classes is now faster than
> creating old-style classes, although both tests are much faster than in
> 2.2. Setting attributes is still somewhat slower. Oh, and the -O flag on
> the CVS build has no measurable effect.
Hi Duncan,
Thanks alot for your time on this. I hadn't read about the __slots__
mechanism before. I feel that there must be something wrong, though,
because the Old object layout is winning! Maybe the implementation is
finding the index of the variable in the __slots__ array and then
using returning __values__[index] in order to implement the attribute
access. I made up the "__values__" array - there must be a values
array somewhere. Array-based object layout amounts to the same thing
as record-based-layout in Python because all primitives are the same
size (right?). There needs to be a way for Python to remember the
indexes... Hmmm. I think your test code doesn't allow the compiler to
remember the indexes since it creates an instance anonymously (using
klass). I tried the code I "attached" at the end of the post:
$ python f.py
2.2.1 (#4, Aug 21 2002, 14:22:21)
[GCC 3.2]
0.14s: 10000 x New()
0.12s: 10000 x New2()
0.09s: 10000 x Old()
13.03s: 10000 x attributes(<class '__main__.New'>)
14.04s: 10000 x attributes(<class '__main__.New2'>)
8.97s: 10000 x attributes(__main__.Old)
13.13s: 10000 x attributesNew()
14.12s: 10000 x attributesNew2()
8.32s: 10000 x attributesOld()
$ python -O f.py
2.2.1 (#4, Aug 21 2002, 14:22:21)
[GCC 3.2]
0.14s: 10000 x New()
0.11s: 10000 x New2()
0.10s: 10000 x Old()
12.18s: 10000 x attributes(<class '__main__.New'>)
12.66s: 10000 x attributes(<class '__main__.New2'>)
7.94s: 10000 x attributes(__main__.Old)
12.26s: 10000 x attributesNew()
12.78s: 10000 x attributesNew2()
7.99s: 10000 x attributesOld()
It made no difference. I checked the bytecode and it is the same for
accessing a slot as for accessing a normal attribute so optimisations
aren't currently possible at that level...
BTW your machine seems very fast. What is it?
Cheers, Steve.
---------------
class Old: pass
class New(object):
__slots__ = ['a', 'b', 'c', 'd']
# New-style, but not using slots.
class New2(object): pass
def timeit(repeat, fn, *args, **kw):
import time
start = time.time()
for i in xrange(repeat):
fn(*args, **kw)
end = time.time()
# Format arguments for printout
argstring = ', '.join(
[str(a) for a in args] +
[ '%s=%s' % (k, v) for k, v in kw.items()])
if len(argstring) > 60:
argstring = argstring[:57] + '...'
print "%.2fs: %d x %s(%s)" % \
((end-start), repeat, fn.__name__, argstring)
def attributes(klass):
inst = klass()
inst.a = 1
for i in xrange(100):
inst.b = inst.a+1
inst.c = inst.a+2
inst.d = inst.a+4
inst.a += 5
def attributesNew():
inst = New()
inst.a = 1
for i in xrange(100):
inst.b = inst.a+1
inst.c = inst.a+2
inst.d = inst.a+4
inst.a += 5
def attributesNew2():
inst = New2()
inst.a = 1
for i in xrange(100):
inst.b = inst.a+1
inst.c = inst.a+2
inst.d = inst.a+4
inst.a += 5
def attributesOld():
inst = Old()
inst.a = 1
for i in xrange(100):
inst.b = inst.a+1
inst.c = inst.a+2
inst.d = inst.a+4
inst.a += 5
def main():
import sys
print sys.version
timeit(10000, New)
timeit(10000, New2)
timeit(10000, Old)
timeit(10000, attributes, New)
timeit(10000, attributes, New2)
timeit(10000, attributes, Old)
timeit(10000, attributesNew)
timeit(10000, attributesNew2)
timeit(10000, attributesOld)
if __name__ == "__main__": main()
More information about the Python-list
mailing list