use TaskThreaded to reduce AI CPU hit

This commit is contained in:
Darren Ranalli 2006-05-16 23:44:20 +00:00
parent 1fa8f88cf9
commit 968c71d5a5

View File

@ -1,6 +1,6 @@
from direct.directnotify import DirectNotifyGlobal from direct.directnotify import DirectNotifyGlobal
from direct.showbase import PythonUtil from direct.showbase import PythonUtil
from direct.showbase.PythonUtil import POD from direct.showbase.TaskThreaded import TaskThreaded
import gc import gc
class FakeObject: class FakeObject:
@ -12,7 +12,7 @@ def _createGarbage():
a.other = b a.other = b
b.other = a b.other = a
class GarbageReport: class GarbageReport(TaskThreaded):
"""Detects leaked Python objects (via gc.collect()) and reports on garbage """Detects leaked Python objects (via gc.collect()) and reports on garbage
items, garbage-to-garbage references, and garbage cycles. items, garbage-to-garbage references, and garbage cycles.
If you just want to dump the report to the log, use GarbageLogger.""" If you just want to dump the report to the log, use GarbageLogger."""
@ -20,9 +20,23 @@ class GarbageReport:
NotGarbage = 'NG' NotGarbage = 'NG'
def __init__(self, name=None, log=True, verbose=False, fullReport=False, findCycles=True): def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True,
threaded=False, doneCallback=None):
# if log is True, GarbageReport will self-destroy after logging # if log is True, GarbageReport will self-destroy after logging
# if false, caller is responsible for calling destroy() # if false, caller is responsible for calling destroy()
# if threaded is True, processing will be performed over multiple frames
TaskThreaded.__init__(self, name, threaded)
# stick the arguments onto a ScratchPad so we can access them from the thread
# functions and delete them all at once
self._args = ScratchPad()
self._args.name = name
self._args.log = log
self._args.verbose = verbose
self._args.fullReport = fullReport
self._args.findCycles = findCycles
self._args.doneCallback = doneCallback
# do the garbage collection
wasOn = PythonUtil.gcDebugOn() wasOn = PythonUtil.gcDebugOn()
oldFlags = gc.get_debug() oldFlags = gc.get_debug()
if not wasOn: if not wasOn:
@ -37,14 +51,17 @@ class GarbageReport:
self.numGarbage = len(self.garbage) self.numGarbage = len(self.garbage)
if verbose: if self._args.verbose:
self.notify.info('found %s garbage items' % self.numGarbage) self.notify.info('found %s garbage items' % self.numGarbage)
self.scheduleNext(self.T_getReferrers)
def T_getReferrers(self):
# grab the referrers (pointing to garbage) # grab the referrers (pointing to garbage)
self.referrersByReference = {} self.referrersByReference = {}
self.referrersByNumber = {} self.referrersByNumber = {}
if fullReport: if self._args.fullReport:
if verbose: if self._args.verbose:
self.notify.info('getting referrers...') self.notify.info('getting referrers...')
# we need referents to detect cycles, but we don't need referrers # we need referents to detect cycles, but we don't need referrers
for i in xrange(self.numGarbage): for i in xrange(self.numGarbage):
@ -52,23 +69,32 @@ class GarbageReport:
self.referrersByNumber[i] = byNum self.referrersByNumber[i] = byNum
self.referrersByReference[i] = byRef self.referrersByReference[i] = byRef
self.scheduleNext(self.T_getReferents)
def T_getReferents(self):
# grab the referents (pointed to by garbage) # grab the referents (pointed to by garbage)
self.referentsByReference = {} self.referentsByReference = {}
self.referentsByNumber = {} self.referentsByNumber = {}
if verbose: if self._args.verbose:
self.notify.info('getting referents...') self.notify.info('getting referents...')
for i in xrange(self.numGarbage): for i in xrange(self.numGarbage):
byNum, byRef = self._getReferents(self.garbage[i]) byNum, byRef = self._getReferents(self.garbage[i])
self.referentsByNumber[i] = byNum self.referentsByNumber[i] = byNum
self.referentsByReference[i] = byRef self.referentsByReference[i] = byRef
self.scheduleNext(self.T_getCycles)
def T_getCycles(self):
# find the cycles # find the cycles
if findCycles and self.numGarbage > 0: if self._args.findCycles and self.numGarbage > 0:
if verbose: if self._args.verbose:
self.notify.info('detecting cycles...') self.notify.info('detecting cycles...')
self.cycles = self._getCycles() self.cycles = self._getCycles()
s = '\n===== GarbageReport: \'%s\' (%s items) =====' % (name, self.numGarbage) self.scheduleNext(self.T_createReport)
def T_createReport(self):
s = '\n===== GarbageReport: \'%s\' (%s items) =====' % (self._args.name, self.numGarbage)
if self.numGarbage > 0: if self.numGarbage > 0:
# log each individual item with a number in front of it # log each individual item with a number in front of it
s += '\n\n===== Garbage Items =====' s += '\n\n===== Garbage Items ====='
@ -81,13 +107,13 @@ class GarbageReport:
for i in range(len(self.garbage)): for i in range(len(self.garbage)):
s += format % (i, type(self.garbage[i]), self.garbage[i]) s += format % (i, type(self.garbage[i]), self.garbage[i])
if findCycles: if self._args.findCycles:
format = '\n%s' format = '\n%s'
s += '\n\n===== Cycles =====' s += '\n\n===== Cycles ====='
for cycle in self.cycles: for cycle in self.cycles:
s += format % cycle s += format % cycle
if fullReport: if self._args.fullReport:
format = '\n%0' + '%s' % digits + 'i:%s' format = '\n%0' + '%s' % digits + 'i:%s'
s += '\n\n===== Referrers By Number (what is referring to garbage item?) =====' s += '\n\n===== Referrers By Number (what is referring to garbage item?) ====='
for i in xrange(self.numGarbage): for i in xrange(self.numGarbage):
@ -103,19 +129,21 @@ class GarbageReport:
s += format % (i, self.referentsByReference[i]) s += format % (i, self.referentsByReference[i])
self._report = s self._report = s
if log:
self.scheduleNext(self.T_printReport)
def T_printReport(self):
if self._args.log:
self.notify.info(self._report) self.notify.info(self._report)
def getNumItems(self): self.scheduleNext(self.T_completed)
return self.numGarbage def T_completed(self):
def getGarbage(self): if self._args.doneCallback:
return self.garbage self._args.doneCallback(self)
def getReport(self):
return self._report
def destroy(self): def destroy(self):
del self._args
del self.garbage del self.garbage
del self.numGarbage del self.numGarbage
del self.referrersByReference del self.referrersByReference
@ -126,6 +154,15 @@ class GarbageReport:
del self.cycles del self.cycles
del self._report del self._report
def getNumItems(self):
return self.numGarbage
def getGarbage(self):
return self.garbage
def getReport(self):
return self._report
def _getReferrers(self, obj): def _getReferrers(self, obj):
# referrers (pointing to garbage) # referrers (pointing to garbage)
# returns two lists, first by index into gc.garbage, second by # returns two lists, first by index into gc.garbage, second by
@ -203,4 +240,6 @@ class GarbageLogger(GarbageReport):
def __init__(self, *args, **kArgs): def __init__(self, *args, **kArgs):
kArgs['log'] = True kArgs['log'] = True
GarbageReport.__init__(self, *args, **kArgs) GarbageReport.__init__(self, *args, **kArgs)
def T_completed(self):
GarbageReport.T_completed(self)
self.destroy() self.destroy()