提交 2bb62ab2 authored 作者: Pascal Lamblin's avatar Pascal Lamblin

Use other impl. of OrderedSet to prevent mem. leak

Previous implementation resulted in memory not being freed, as exhibited in particular on theano/sandbox/cuda/tests/test_memory.py. The version for older Python, as well as the new one, appear to work correctly on that test.
上级 7790833a
...@@ -19,9 +19,7 @@ def check_deterministic(iterable): ...@@ -19,9 +19,7 @@ def check_deterministic(iterable):
list, tuple, OrderedSet, types.GeneratorType, basestring)) list, tuple, OrderedSet, types.GeneratorType, basestring))
if MutableSet is not None: if MutableSet is not None:
# From http://code.activestate.com/recipes/576694/
# Copyright (C) 2009 Raymond Hettinger # Copyright (C) 2009 Raymond Hettinger
# Permission is hereby granted, free of charge, to any person obtaining a # Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the # copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including # "Software"), to deal in the Software without restriction, including
...@@ -40,9 +38,22 @@ if MutableSet is not None: ...@@ -40,9 +38,22 @@ if MutableSet is not None:
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
KEY, PREV, NEXT = range(3) ## {{{ http://code.activestate.com/recipes/576696/ (r5)
import collections
class OrderedSet(MutableSet): from weakref import proxy
class Link(object):
__slots__ = 'prev', 'next', 'key', '__weakref__'
class OrderedSet(collections.MutableSet):
'Set the remembers the order elements were added'
# Big-O running times for all methods are the same as for regular sets.
# The internal self.__map dictionary maps keys to links in a doubly linked list.
# The circular doubly linked list starts and ends with a sentinel element.
# The sentinel element never gets deleted (this simplifies the algorithm).
# The prev/next links are weakref proxies (to prevent circular references).
# Individual links are kept alive by the hard reference in self.__map.
# Those hard references disappear when a key is deleted from an OrderedSet.
# Added by IG-- pre-existing theano code expected sets # Added by IG-- pre-existing theano code expected sets
# to have this method # to have this method
...@@ -51,53 +62,55 @@ if MutableSet is not None: ...@@ -51,53 +62,55 @@ if MutableSet is not None:
self |= iterable self |= iterable
def __init__(self, iterable=None): def __init__(self, iterable=None):
# Checks added by IG self.__root = root = Link() # sentinel node for doubly linked list
check_deterministic(iterable) root.prev = root.next = root
self.end = end = [] self.__map = {} # key --> link
end += [None, end, end] # sentinel node for doubly linked list
self.map = {} # key --> [key, prev, next]
if iterable is not None: if iterable is not None:
self |= iterable self |= iterable
def __len__(self): def __len__(self):
return len(self.map) return len(self.__map)
def __contains__(self, key): def __contains__(self, key):
return key in self.map return key in self.__map
def add(self, key): def add(self, key):
if key not in self.map: # Store new key in a new link at the end of the linked list
end = self.end if key not in self.__map:
curr = end[PREV] self.__map[key] = link = Link()
curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end] root = self.__root
last = root.prev
link.prev, link.next, link.key = last, root, key
last.next = root.prev = proxy(link)
def discard(self, key): def discard(self, key):
if key in self.map: # Remove an existing item using self.__map to find the link which is
key, prev, next = self.map.pop(key) # then removed by updating the links in the predecessor and successors.
prev[NEXT] = next if key in self.__map:
next[PREV] = prev link = self.__map.pop(key)
link.prev.next = link.next
link.next.prev = link.prev
def __iter__(self): def __iter__(self):
end = self.end # Traverse the linked list in order.
curr = end[NEXT] root = self.__root
while curr is not end: curr = root.next
yield curr[KEY] while curr is not root:
curr = curr[NEXT] yield curr.key
curr = curr.next
def __reversed__(self): def __reversed__(self):
end = self.end # Traverse the linked list in reverse order.
curr = end[PREV] root = self.__root
while curr is not end: curr = root.prev
yield curr[KEY] while curr is not root:
curr = curr[PREV] yield curr.key
curr = curr.prev
def pop(self, last=True): def pop(self, last=True):
if not self: if not self:
raise KeyError('set is empty') raise KeyError('set is empty')
if last: key = next(reversed(self)) if last else next(iter(self))
key = next(reversed(self))
else:
key = next(iter(self))
self.discard(key) self.discard(key)
return key return key
...@@ -123,8 +136,8 @@ if MutableSet is not None: ...@@ -123,8 +136,8 @@ if MutableSet is not None:
else: else:
return NotImplemented return NotImplemented
def __del__(self): ## end of http://code.activestate.com/recipes/576696/ }}}
self.clear() # remove circular references
else: else:
# Python 2.4 # Python 2.4
class OrderedSet(object): class OrderedSet(object):
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论