Re: zope.interface memory optimization

Brian Sutherland <[email protected]>
Newsgroups gmane.comp.web.zope.devel
Message-ID <[email protected]>
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  -  [email protected]
> 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

_______________________________________________
Zope-Dev maillist  -  [email protected]
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 )
bench_if_creation.py (text/plain, 2.6 KB)
#
# 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)])
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.