Are weak refs slower than strong refs?

Duncan Booth duncan.booth at invalid.invalid
Sun Feb 25 12:12:55 EST 2007


John Nagle <nagle at animats.com> wrote:

> Are weak refs slower than strong refs?  I've been considering
> making the "parent" links in BeautifulSoup into weak refs, so the
> trees will release immediately when they're no longer needed.  In
> general, all links back towards the root of a tree should be weak
> refs; this breaks the loops that give reference counting trouble.

Yes, weak references are slower, but that may not be significant unless 
people are following the parent links a lot: I would expect other 
processing to make the overhead fairly insignificant.

Why not do a few timing tests with some typical use cases? Here's an 
example which does nothing much but create and follow. Typical output:

Using proxy
100 loops, best of 3: 5.6 msec per loop
Call to dummy proxy
100 loops, best of 3: 3.11 msec per loop
Without proxy call
100 loops, best of 3: 2.71 msec per loop

------ timetest.py --------------------------------------------------------
from timeit import Timer, default_timer as timer
from weakref import proxy

class C:
    '''Use a weak proxy (or whatever 'proxy' returns)'''
    def __init__(self, parent=None):
        self.parent = proxy(parent) if parent is not None else parent
        self.child = None

    def show_parents(self):
        while self is not None:
            # print "show_parents", id(self)
            self = self.parent

    def show_children(self):
        while self is not None:
            # print "show_children", id(self)
            self = self.child


def t1(n):
    base = C()
    current = base
    for i in range(n):
        current.child = C(current)
        current = current.child
    base.show_children()
    current.show_parents()

class D:
    '''Strong parent reference'''
    def __init__(self, parent=None):
        self.parent = parent if parent is not None else parent
        self.child = None

    def show_parents(self):
        while self is not None:
            # print "show_parents", id(self)
            self = self.parent

    def show_children(self):
        while self is not None:
            # print "show_children", id(self)
            self = self.child

def t2(n):
    base = D()
    current = base
    for i in range(n):
        current.child = D(current)
        current = current.child
    base.show_children()
    current.show_parents()


def timetest(stmt):
    repeat = 3
    precision = 3
    t = Timer(stmt, 'import __main__', timer)

    # determine number so that 0.2 <= total time < 2.0
    for i in range(1, 10):
        number = 10**i
        try:
            x = t.timeit(number)
        except:
            t.print_exc()
            return 1
        if x >= 0.2:
            break

    try:
        r = t.repeat(repeat, number)
    except:
        t.print_exc()
        return 1
    best = min(r)
    print "%d loops," % number,
    usec = best * 1e6 / number
    if usec < 1000:
        print "best of %d: %.*g usec per loop" % (repeat, precision, usec)
    else:
        msec = usec / 1000
        if msec < 1000:
            print "best of %d: %.*g msec per loop" % (repeat, precision, 
msec)
        else:
            sec = msec / 1000
            print "best of %d: %.*g sec per loop" % (repeat, precision, 
sec)

if __name__=='__main__':
    print "Using proxy"
    timetest('__main__.t1(1000)')
    print "Call to dummy proxy"
    def proxy(x): return x
    timetest('__main__.t1(1000)')
    print "Without proxy call"
    timetest('__main__.t2(1000)')
 -------------------------------------------------------------------------



More information about the Python-list mailing list