map del efficiency python2.2 and 2.1

Andrew McNamara andrewm at object-craft.com.au
Wed Jul 17 00:11:33 EDT 2002


>This is exactly the kind of "seemingly random disaster" we see when the
>platform free() goes nuts, although it's usually much more pronounced than a
>measly factor of 2.  If so, rebuilding with pymalloc is likely to fix it,
>while if you don't you'll never answer "why?" without a deep understanding
>of your platform C's implementation of free().

I recently got curious about the relative performance of various python
versions, new style classes and slots. I wrote a very simple benchmark
that allocated and deallocated a million small objects on a 600MHz P3
running linux 2.4.18 and glibc-2.2.4 with enough memory to prevent paging.

Results and code are below. It's clear that the system malloc hurts
badly for deallocation, but even with pymalloc deallocation is still
an expensive exercise (the attribute dictionary, presumably). Slots are
very efficient for small objects, in terms of CPU and memory footprint.

               Create and       
                 set attr   Set attr     Del+GC       RSS
                 --------   --------   --------   -------
py22  slots         20.3s       4.6s      19.5s       48M
py221 slots         21.0s       5.0s      21.5s       48M
pymalloc slots      16.2s       4.7s       0.7s       40M

py22  obj           65.9s       7.4s     401.5s      185M
py221 obj           62.1s       7.3s     492.8s      185M
pymalloc obj        57.9s       7.2s     125.8s      185M

py22  trad          55.8s       6.8s     275.8s      192M
py221 trad          56.8s       7.1s     284.7s      192M
pymalloc trad       52.6s       6.8s     115.0s      185M

py211 trad          52.6s       6.6s     239.4s      154M

With gc.disable(), and explicitly calling gc.collect():

               Create and       
                 set attr   Set attr     Del+GC       RSS
                 --------   --------   --------   -------
py22  slots         13.6s       4.6s      19.7s       48M
py221 slots         14.0s       5.0s      19.4s       48M
pymalloc slots      10.6s       4.7s       0.7s       40M

py22  obj           18.0s       7.3s     400.5s      185M
py221 obj           18.7s       7.6s     399.0s      185M
pymalloc obj        16.2s       7.2s     117.0s      185M

py22  trad          11.2s       6.8s     270.8s      192M
py221 trad          12.1s       7.2s     240.7s      192M
pymalloc trad       11.4s       6.8s     115.3s      185M

py211 trad          11.6s       6.5s     229.0s      154M

py152 trad           7.8s       3.1s      75.5s      123M


KEY
------   -----------------------------------------------------------
slots    with new-style objects and using slots
obj      with new-style objects
trad     with traditional objects
py22     Python 2.2
py221    Python 2.2.1
pymalloc Python 2.2.1 compiled with --with-pymalloc
py211    Python 2.1.1 with traditional objects
py152    Python 1.5.2 with traditional objects (no cyclic-GC)

----------------------------------------------------------------------------

    import time, gc

    iters = 1000000

    def start():
        global ts

        ts = time.time()

    def stop(msg):
        delta = time.time() - ts

        if delta < 0.00001:
            print msg, "took %.1fus" % (delta * 1000000)
        elif delta < 0.01:
            print msg, "took %.1fms" % (delta * 1000)
        else:
            print msg, "took %.1fs" % (delta)

    try:
        class slots(object):
            __slots__ = ('a')

            def __init__(self, n):
                self.a = n

            def set(self, x):
                self.a = x

        class obj(object):
            def __init__(self, n):
                self.a = n

            def set(self, x):
                self.a = x

    except NameError:
        pass

    class trad:
        def __init__(self, n):
            self.a = n

        def set(self, x):
            self.a = x


    def testit(ctor, count):
        print "testing", ctor.__name__

        start()
        a = map(ctor, xrange(count))
        gc.collect()
        stop(' create + setattr')

        start()
        aa = ctor(0)
        map(aa.set, xrange(count))
        gc.collect()
        stop(' setattr')

        start()
        del a
        gc.collect()
        stop(' del')

    if 0:
        gc.disable()

    if 0:
        testit(slots, iters)

    if 0:
        testit(obj, iters)

    if 1:
        testit(trad, iters)


-- 
Andrew McNamara, Senior Developer, Object Craft
http://www.object-craft.com.au/





More information about the Python-list mailing list