[Zope-dev] zope.interface memory optimization
Brian Sutherland
brian at vanguardistas.net
Tue Nov 9 09:28:38 EST 2010
On Tue, Nov 09, 2010 at 01:21:00AM +0200, Marius Gedminas wrote:
> On Mon, Nov 08, 2010 at 03:35:09PM +0100, Brian Sutherland wrote:
> > I've committed 2 patches to a "jinty-mem" branch of zope.interface.
> > Together these patches reduce the startup memory use of my ZTK based
> > application by 3%. Is that enough to justify their risk/complexity?
> >
> > https://mail.zope.org/pipermail/checkins/2010-November/052516.html
> > https://mail.zope.org/pipermail/checkins/2010-November/052517.html
> >
> > If no-one replies, I'll assume that 3% is just not enough to be
> > interesting and do nothing;)
>
> It's so interesting I'm going to ask for even more numbers: how much is
> that 3% in kilobytes, or objects?
Ok, you asked for it ;)
The 3% was in kilobytes as measured using /bin/ps on OSX leopard with
python2.6 from MacPorts. The app I measured is a ZTK 1.0 based web
application.
My application without the changes:
Objects: 245614
RSS memory (kb): 59116
My application with the changes:
Objects: 227147
RSS memory (kb): 57180
I also did a micro-benchmark (more below) which indicate that when
creating an interface with 5 attributes and 5 methods you save 41% in
kilobytes and 34% in objects.
> Is there any measurable speed impact
> for application startup or test runs (faster/slower)?
My app doesn't startup measurably faster or slower. The ZTK tests seem
to run a little faster, but the error is high on my laptop:
Without patches:
./bin/test-ztk 71.93s
./bin/test-ztk 70.49s
./bin/test-ztk 70.31s
With patches:
./bin/test-ztk 70.28s
./bin/test-ztk 70.00s
./bin/test-ztk 69.98s
I also did some micro-benchmarking (script is attached). There were
repeatable results which indicate that startup will be faster and
runtime only probably faster:
Without patches:
Performance bench
-----------------
one interface, 5 methods, 5 functions: 181.00 usec/pass
five inheriting interfaces: 355.76 usec/pass
one interface: 59.79 usec/pass
query uninitialized tagged value: 1.26 usec/pass
query initialized tagged value: 1.27 usec/pass
Memory bench
------------
Memory before (kb):
RSS VSZ
33684 109780
objects created: 150001
Memory after (kb):
RSS VSZ
47856 124116
With patches:
Performance bench
-----------------
one interface, 5 methods, 5 functions: 165.97 usec/pass
five inheriting interfaces: 341.22 usec/pass
one interface: 53.03 usec/pass
query uninitialized tagged value: 0.75 usec/pass
query initialized tagged value: 1.37 usec/pass
Memory bench
------------
Memory before (kb):
RSS VSZ
33328 109524
objects created: 99001
Memory after (kb):
RSS VSZ
41728 117972
> Otherwise I'm +1, assuming this doesn't somehow make the code 10%
> slower.
>
> Marius Gedminas
> --
> http://pov.lt/ -- Zope 3/BlueBream consulting and development
> _______________________________________________
> Zope-Dev maillist - Zope-Dev at zope.org
> https://mail.zope.org/mailman/listinfo/zope-dev
> ** No cross posts or HTML encoding! **
> (Related lists -
> https://mail.zope.org/mailman/listinfo/zope-announce
> https://mail.zope.org/mailman/listinfo/zope )
--
Brian Sutherland
-------------- next part --------------
#
# Perdormance Benchmark
#
import os
import timeit
import subprocess
from zope.interface import Interface, Attribute
print "Performance bench"
print "-----------------"
s1 = """\
class I1(Interface):
a1 = Attribute('one')
a2 = Attribute('two')
a3 = Attribute('thee')
a4 = Attribute('four')
a5 = Attribute('five')
def f1(arg):
pass
def f2(arg):
pass
def f3(arg):
pass
def f4(arg):
pass
def f5(arg):
pass
"""
t = timeit.Timer(stmt=s1, setup="from zope.interface import Interface, Attribute")
print "one interface, 5 methods, 5 functions: %.2f usec/pass" % (1000000 * t.timeit(number=10000)/10000)
s1 = """\
class I1(Interface):
pass
class I2(I1):
pass
class I3(I2):
pass
class I4(I3):
pass
class I5(I4):
pass
"""
t = timeit.Timer(stmt=s1, setup="from zope.interface import Interface, Attribute")
print "five inheriting interfaces: %.2f usec/pass" % (1000000 * t.timeit(number=10000)/10000)
s1 = """\
class I1(Interface):
pass
"""
t = timeit.Timer(stmt=s1, setup="from zope.interface import Interface, Attribute")
print "one interface: %.2f usec/pass" % (1000000 * t.timeit(number=10000)/10000)
setup = """\
from zope.interface import Interface, Attribute
class I1(Interface):
pass
"""
s1 = """\
I1.queryTaggedValue('bob')
"""
t = timeit.Timer(stmt=s1, setup=setup)
print "query uninitialized tagged value: %.2f usec/pass" % (1000000 * t.timeit(number=50000)/50000)
setup = """\
from zope.interface import Interface, Attribute
class I1(Interface):
pass
I1.setTaggedValue('bob', 'bobby')
"""
s1 = """\
I1.queryTaggedValue('bob')
"""
t = timeit.Timer(stmt=s1, setup=setup)
print "query initialized tagged value: %.2f usec/pass" % (1000000 * t.timeit(number=50000)/50000)
print
print "Memory bench"
print "------------"
pid = os.getpid()
import gc
gc.collect()
gc.collect()
gc.collect()
print 'Memory before (kb):'
subprocess.call(['/bin/ps', '-o', 'rss,vsize', str(pid)])
before_objects = len(gc.get_objects())
log = []
for i in range(3000):
class I1(Interface):
a1 = Attribute('one')
a2 = Attribute('two')
a3 = Attribute('thee')
a4 = Attribute('four')
a5 = Attribute('five')
def f1(arg):
pass
def f2(arg):
pass
def f3(arg):
pass
def f4(arg):
pass
def f5(arg):
pass
log.append(I1)
gc.collect()
gc.collect()
gc.collect()
print "objects created: %s" % (len(gc.get_objects()) - before_objects)
print 'Memory after (kb):'
subprocess.call(['/bin/ps', '-o', 'rss,vsize', str(pid)])
More information about the Zope-Dev
mailing list