John Nagle said:
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)')
-------------------------------------------------------------------------