first rev

This commit is contained in:
Darren Ranalli 2006-06-23 21:22:42 +00:00
parent 2360220a99
commit ce8589e0fa
2 changed files with 266 additions and 0 deletions

115
direct/src/showbase/ObjectPool.py Executable file
View File

@ -0,0 +1,115 @@
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import invertDict, makeList, safeRepr
from direct.showbase.PythonUtil import getNumberedTypedString
import types
import gc
class Diff:
def __init__(self, lost, gained):
self.lost=lost
self.gained=gained
def __repr__(self):
s = 'lost %s objects, gained %s objects' % (len(self.lost), len(self.gained))
s += '\n\nself.lost\n'
s += self.lost.typeFreqStr()
s += '\n\nself.gained\n'
s += self.gained.typeFreqStr()
s += '\n\nGAINED-OBJECT REFERRERS\n'
s += self.gained.referrersStr(1)
return s
class ObjectPool:
"""manipulate a pool of Python objects"""
notify = directNotify.newCategory('ObjectPool')
def __init__(self, objects):
self._objs = list(objects)
self._type2objs = {}
self._count2types = {}
type2count = {}
for obj in self._objs:
typ = type(obj)
# <type 'instance'> isn't all that useful
if typ is types.InstanceType:
typ = '%s of %s' % (typ, repr(obj.__class__))
type2count.setdefault(typ, 0)
type2count[typ] += 1
self._type2objs.setdefault(typ, [])
self._type2objs[typ].append(obj)
self._count2types = invertDict(type2count)
def _getInternalObjs(self):
return (self._objs, self._type2objs, self._count2types)
def destroy(self):
del self._objs
del self._type2objs
del self._count2types
def getTypes(self):
return self._type2objs.keys()
def getObjsOfType(self, type):
return self._type2objs.get(type, [])
def printObjsOfType(self, type):
for obj in self._type2objs.get(type, []):
print repr(obj)
def diff(self, other):
"""print difference between this pool and 'other' pool"""
thisId2obj = {}
otherId2obj = {}
for obj in self._objs:
thisId2obj[id(obj)] = obj
for obj in other._objs:
otherId2obj[id(obj)] = obj
thisIds = set(thisId2obj.keys())
otherIds = set(otherId2obj.keys())
lostIds = thisIds.difference(otherIds)
print 'lost: %s' % lostIds
gainedIds = otherIds.difference(thisIds)
del thisIds
del otherIds
lostObjs = []
for i in lostIds:
lostObjs.append(thisId2obj[i])
gainedObjs = []
for i in gainedIds:
gainedObjs.append(otherId2obj[i])
return Diff(self.__class__(lostObjs), self.__class__(gainedObjs))
def typeFreqStr(self):
s = 'Object Pool: Type Frequencies'
s += '\n============================='
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
types = makeList(self._count2types[count])
for typ in types:
s += '\n%s\t%s' % (count, typ)
return s
def referrersStr(self, numEach=3):
"""referrers of the first few of each type of object"""
s = ''
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
types = makeList(self._count2types[count])
for typ in types:
s += '\n\nTYPE: %s' % typ
for i in xrange(min(numEach,len(self._type2objs[typ]))):
obj = self._type2objs[typ][i]
s += '\nOBJ: %s\n' % safeRepr(obj)
referrers = gc.get_referrers(obj)
if len(referrers):
s += getNumberedTypedString(referrers)
else:
s += '<No Referrers>'
return s
def __len__(self):
return len(self._objs)

View File

@ -0,0 +1,151 @@
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase import DirectObject, ObjectPool
from direct.showbase.PythonUtil import makeList, Sync
import gc
import sys
"""
>>> from direct.showbase import ObjectReport
>>> o=ObjectReport.ObjectReport('baseline')
>>> run()
...
>>> o2=ObjectReport.ObjectReport('')
>>> o.diff(o2)
<wait a loooong time>
"""
class ExclusiveObjectPool(DirectObject.DirectObject):
"""ObjectPool specialization that excludes particular objects"""
# IDs of objects to globally exclude from reporting
_ExclObjIds = {}
_SyncMaster = Sync('ExclusiveObjectPool.ExcludedObjectList')
_SerialNumGen = SerialNumGen()
@classmethod
def addExclObjs(cls, *objs):
for obj in makeList(objs):
cls._ExclObjIds.setdefault(id(obj), 0)
cls._ExclObjIds[id(obj)] += 1
cls._SyncMaster.change()
@classmethod
def removeExclObjs(cls, *objs):
for obj in makeList(objs):
assert id(obj) in cls._ExclObjIds
del cls._ExclObjIds[id(obj)]
cls._SyncMaster.change()
def __init__(self, objects):
self._objects = list(objects)
self._postFilterObjs = []
self._sync = Sync('%s-%s' % (self.__class__.__name__,
self._SerialNumGen.next()),
self._SyncMaster)
self._sync.invalidate()
ExclusiveObjectPool.addExclObjs(self._objects, self._postFilterObjs,
self._sync)
def destroy(self):
self.ignoreAll()
ExclusiveObjectPool.removeExclObjs(self._objects, self._postFilterObjs,
self._sync)
del self._objects
del self._postFilterObjs
del self._sync
def _resync(self):
if self._sync.isSynced(self._SyncMaster):
return
if hasattr(self, '_filteredPool'):
ExclusiveObjectPool.removeExclObjs(*self._filteredPool._getInternalObjs())
ExclusiveObjectPool.removeExclObjs(self._filteredPool)
del self._filteredPool
del self._postFilterObjs[:]
for obj in self._objects:
if id(obj) not in ExclusiveObjectPool._ExclObjIds:
self._postFilterObjs.append(obj)
self._filteredPool = ObjectPool.ObjectPool(self._postFilterObjs)
ExclusiveObjectPool.addExclObjs(self._filteredPool)
ExclusiveObjectPool.addExclObjs(*self._filteredPool._getInternalObjs())
self._sync.sync(self._SyncMaster)
def getObjsOfType(self, type):
self._resync()
return self._filteredPool.getObjsOfType(type)
def printObjsOfType(self, type):
self._resync()
return self._filteredPool.printObjsOfType(type)
def diff(self, other):
self._resync()
return self._filteredPool.diff(other._filteredPool)
def typeFreqStr(self):
self._resync()
return self._filteredPool.typeFreqStr()
def __len__(self):
self._resync()
return len(self._filteredPool)
class ObjectReport:
"""report on every Python object in the current process"""
notify = directNotify.newCategory('ObjectReport')
def __init__(self, name, log=True, garbageCollect=True):
if not hasattr(sys, 'getobjects'):
self.notify.error(
'%s only works in debug Python (pyd-shell)' % self.__class__.__name__)
if garbageCollect:
import gc
gc.collect()
self._name = name
self._pool = ExclusiveObjectPool(self._getObjectList())
ExclusiveObjectPool.addExclObjs(self, self._pool, self._name)
if log:
self.notify.info('===== ObjectReport: \'%s\' =====\n%s' % (self._name, self.typeFreqStr()))
def destroy(self):
ExclusiveObjectPool.removeExclObjs(self, self._pool, self._name)
self._pool.destroy()
del self._pool
del self._name
def typeFreqStr(self):
return self._pool.typeFreqStr()
def diff(self, other):
return self._pool.diff(other._pool)
def _getObjectList(self):
if hasattr(sys, 'getobjects'):
return sys.getobjects(0)
else:
objs = []
stateStack = Stack()
root = __builtins__
objIds = set([id(root)])
stateStack.push((root, None, 0))
while True:
if len(stateStack) == 0:
break
obj, adjacents, resumeIndex = stateStack.pop()
objs.append(obj)
print id(obj)
if adjacents is None:
adjacents = gc.get_referents(obj)
adjacents.extend(gc.get_referrers(obj))
# pare the list down to the ones that have not already been visited
# to minimize calls to get_referents/get_referrers
newObjs = []
for adj in adjacents:
adjId = id(adj)
if adjId not in objIds:
objIds.add(adjId)
newObjs.append(adj)
adjacents = newObjs
if len(adjacents) == 0:
print 'DEAD END'
for i in xrange(resumeIndex, len(adjacents)):
adj = adjacents[i]
stateStack.push((obj, adjacents, i+1))
stateStack.push((adj, None, 0))
continue