[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