mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
first rev
This commit is contained in:
parent
2360220a99
commit
ce8589e0fa
115
direct/src/showbase/ObjectPool.py
Executable file
115
direct/src/showbase/ObjectPool.py
Executable 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)
|
151
direct/src/showbase/ObjectReport.py
Executable file
151
direct/src/showbase/ObjectReport.py
Executable 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
|
Loading…
x
Reference in New Issue
Block a user