mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
show timings as percentages, don't display max-non-spike profiles, output easier to scan
This commit is contained in:
parent
f7fc1ca70b
commit
232583858c
@ -12,6 +12,11 @@ import marshal
|
|||||||
class PercentStats(pstats.Stats):
|
class PercentStats(pstats.Stats):
|
||||||
# prints more useful output when sampled durations are shorter than a millisecond
|
# prints more useful output when sampled durations are shorter than a millisecond
|
||||||
# lots of this is copied from Python's pstats.py
|
# lots of this is copied from Python's pstats.py
|
||||||
|
def setTotalTime(self, tt):
|
||||||
|
# use this to set 'total time' to base time percentages on
|
||||||
|
# allows profiles to show timing based on percentages of duration of another profile
|
||||||
|
self._totalTime = tt
|
||||||
|
|
||||||
def print_stats(self, *amount):
|
def print_stats(self, *amount):
|
||||||
for filename in self.files:
|
for filename in self.files:
|
||||||
print filename
|
print filename
|
||||||
@ -26,6 +31,8 @@ class PercentStats(pstats.Stats):
|
|||||||
# DCR
|
# DCR
|
||||||
#print "in %.3f CPU seconds" % self.total_tt
|
#print "in %.3f CPU seconds" % self.total_tt
|
||||||
print "in %s CPU milliseconds" % (self.total_tt * 1000.)
|
print "in %s CPU milliseconds" % (self.total_tt * 1000.)
|
||||||
|
if self._totalTime != self.total_tt:
|
||||||
|
print indent, 'percentages are of %s CPU milliseconds' % (self._totalTime * 1000)
|
||||||
print
|
print
|
||||||
width, list = self.get_print_list(amount)
|
width, list = self.get_print_list(amount)
|
||||||
if list:
|
if list:
|
||||||
@ -33,12 +40,15 @@ class PercentStats(pstats.Stats):
|
|||||||
for func in list:
|
for func in list:
|
||||||
self.print_line(func)
|
self.print_line(func)
|
||||||
print
|
print
|
||||||
print
|
# DCR
|
||||||
|
#print
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@staticmethod
|
def f8(self, x):
|
||||||
def f8(x):
|
if self._totalTime == 0.:
|
||||||
return "%7.2f%%" % (x*100.)
|
# profiling was too quick for clock resolution...
|
||||||
|
return ' Inf%'
|
||||||
|
return "%7.2f%%" % ((x*100.) / self._totalTime)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def func_std_string(func_name): # match what old profile produced
|
def func_std_string(func_name): # match what old profile produced
|
||||||
@ -47,21 +57,23 @@ class PercentStats(pstats.Stats):
|
|||||||
def print_line(self, func):
|
def print_line(self, func):
|
||||||
cc, nc, tt, ct, callers = self.stats[func]
|
cc, nc, tt, ct, callers = self.stats[func]
|
||||||
c = str(nc)
|
c = str(nc)
|
||||||
d = self.total_tt
|
# DCR
|
||||||
f8 = PercentStats.f8
|
f8 = self.f8
|
||||||
if nc != cc:
|
if nc != cc:
|
||||||
c = c + '/' + str(cc)
|
c = c + '/' + str(cc)
|
||||||
print c.rjust(9),
|
print c.rjust(9),
|
||||||
print f8(tt/d),
|
print f8(tt),
|
||||||
if nc == 0:
|
if nc == 0:
|
||||||
print ' '*8,
|
print ' '*8,
|
||||||
else:
|
else:
|
||||||
print f8((tt/nc)/d),
|
print f8(tt/nc),
|
||||||
print f8(ct/d),
|
print f8(ct),
|
||||||
if cc == 0:
|
if cc == 0:
|
||||||
print ' '*8,
|
print ' '*8,
|
||||||
else:
|
else:
|
||||||
print f8((ct/cc)/d),
|
print f8(ct/cc),
|
||||||
|
# DCR
|
||||||
|
#print func_std_string(func)
|
||||||
print PercentStats.func_std_string(func)
|
print PercentStats.func_std_string(func)
|
||||||
|
|
||||||
class ProfileSession:
|
class ProfileSession:
|
||||||
@ -204,9 +216,10 @@ class ProfileSession:
|
|||||||
del self._filename2ramFile[filename]
|
del self._filename2ramFile[filename]
|
||||||
|
|
||||||
def getResults(self,
|
def getResults(self,
|
||||||
lines=80,
|
lines=200,
|
||||||
sorts=['cumulative', 'time', 'calls'],
|
sorts=['cumulative', 'time', 'calls'],
|
||||||
callInfo=False):
|
callInfo=False,
|
||||||
|
totalTime=None):
|
||||||
if not self.profileSucceeded():
|
if not self.profileSucceeded():
|
||||||
output = '%s: profiler already running, could not profile' % self._name
|
output = '%s: profiler already running, could not profile' % self._name
|
||||||
else:
|
else:
|
||||||
@ -232,11 +245,15 @@ class ProfileSession:
|
|||||||
# throw out any cached result strings
|
# throw out any cached result strings
|
||||||
self._resultCache = {}
|
self._resultCache = {}
|
||||||
|
|
||||||
|
if totalTime is None:
|
||||||
|
totalTime = self._stats.total_tt
|
||||||
|
|
||||||
# make sure the arguments will hash efficiently if callers provide different types
|
# make sure the arguments will hash efficiently if callers provide different types
|
||||||
lines = int(lines)
|
lines = int(lines)
|
||||||
sorts = list(sorts)
|
sorts = list(sorts)
|
||||||
callInfo = bool(callInfo)
|
callInfo = bool(callInfo)
|
||||||
k = str((lines, sorts, callInfo))
|
totalTime = float(totalTime)
|
||||||
|
k = str((lines, sorts, callInfo, totalTime))
|
||||||
if k in self._resultCache:
|
if k in self._resultCache:
|
||||||
# we've already created this output string, get it from the cache
|
# we've already created this output string, get it from the cache
|
||||||
output = self._resultCache[k]
|
output = self._resultCache[k]
|
||||||
@ -248,6 +265,8 @@ class ProfileSession:
|
|||||||
|
|
||||||
# print the info to stdout
|
# print the info to stdout
|
||||||
s = self._stats
|
s = self._stats
|
||||||
|
# make sure our percentages are relative to the correct total time
|
||||||
|
s.setTotalTime(totalTime)
|
||||||
for sort in sorts:
|
for sort in sorts:
|
||||||
s.sort_stats(sort)
|
s.sort_stats(sort)
|
||||||
s.print_stats(lines)
|
s.print_stats(lines)
|
||||||
|
@ -11,7 +11,6 @@ class TaskTracker:
|
|||||||
self._namePrefix = namePrefix
|
self._namePrefix = namePrefix
|
||||||
self._durationAverager = Averager('%s-durationAverager' % namePrefix)
|
self._durationAverager = Averager('%s-durationAverager' % namePrefix)
|
||||||
self._avgSession = None
|
self._avgSession = None
|
||||||
self._maxNonSpikeSession = None
|
|
||||||
if TaskTracker.MinSamples is None:
|
if TaskTracker.MinSamples is None:
|
||||||
# number of samples required before spikes start getting identified
|
# number of samples required before spikes start getting identified
|
||||||
TaskTracker.MinSamples = config.GetInt('profile-task-spike-min-samples', 30)
|
TaskTracker.MinSamples = config.GetInt('profile-task-spike-min-samples', 30)
|
||||||
@ -25,10 +24,7 @@ class TaskTracker:
|
|||||||
self._durationAverager.reset()
|
self._durationAverager.reset()
|
||||||
if self._avgSession:
|
if self._avgSession:
|
||||||
self._avgSession.release()
|
self._avgSession.release()
|
||||||
if self._maxNonSpikeSession:
|
|
||||||
self._maxNonSpikeSession.release()
|
|
||||||
self._avgSession = None
|
self._avgSession = None
|
||||||
self._maxNonSpikeSession = None
|
|
||||||
def getNamePrefix(self, namePrefix):
|
def getNamePrefix(self, namePrefix):
|
||||||
return self._namePrefix
|
return self._namePrefix
|
||||||
def _checkSpike(self, session):
|
def _checkSpike(self, session):
|
||||||
@ -40,51 +36,33 @@ class TaskTracker:
|
|||||||
if duration > (self.getAvgDuration() * self.SpikeThreshold):
|
if duration > (self.getAvgDuration() * self.SpikeThreshold):
|
||||||
isSpike = True
|
isSpike = True
|
||||||
avgSession = self.getAvgSession()
|
avgSession = self.getAvgSession()
|
||||||
maxNSSession = self.getMaxNonSpikeSession()
|
s = '\n%s task CPU spike profile (%s) %s\n' % ('=' * 30, self._namePrefix, '=' * 30)
|
||||||
self.notify.info('task CPU spike profile (%s):\n'
|
s += ('~' * 60) + '\n'
|
||||||
'== AVERAGE\n%s\n'
|
for sorts in (['cumulative'], ['time'], ['calls']):
|
||||||
'== LONGEST NON-SPIKE\n%s\n'
|
s += ('-- AVERAGE --\n%s'
|
||||||
'== SPIKE\n%s' % (
|
'-- SPIKE --\n%s' % (
|
||||||
self._namePrefix,
|
avgSession.getResults(sorts=sorts, totalTime=duration),
|
||||||
avgSession.getResults(),
|
session.getResults(sorts=sorts)))
|
||||||
maxNSSession.getResults(),
|
self.notify.info(s)
|
||||||
session.getResults()))
|
|
||||||
return isSpike
|
return isSpike
|
||||||
def addProfileSession(self, session):
|
def addProfileSession(self, session):
|
||||||
isSpike = self._checkSpike(session)
|
isSpike = self._checkSpike(session)
|
||||||
duration = session.getDuration()
|
duration = session.getDuration()
|
||||||
self._durationAverager.addValue(duration)
|
self._durationAverager.addValue(duration)
|
||||||
if self.getNumDurationSamples() == self.MinSamples:
|
|
||||||
# if we just collected enough samples to check for spikes, see if
|
|
||||||
# our max 'non-spike' session collected so far counts as a spike
|
|
||||||
# and if so, log it
|
|
||||||
if self._checkSpike(self._maxNonSpikeSession):
|
|
||||||
self._maxNonSpikeSession = self._avgSession
|
|
||||||
|
|
||||||
storeAvg = True
|
storeAvg = True
|
||||||
storeMaxNS = True
|
|
||||||
if self._avgSession is not None:
|
if self._avgSession is not None:
|
||||||
avgDur = self.getAvgDuration()
|
avgDur = self.getAvgDuration()
|
||||||
if abs(self._avgSession.getDuration() - avgDur) < abs(duration - avgDur):
|
if abs(self._avgSession.getDuration() - avgDur) < abs(duration - avgDur):
|
||||||
# current avg data is more average than this new sample, keep the data we've
|
# current avg data is more average than this new sample, keep the data we've
|
||||||
# already got stored
|
# already got stored
|
||||||
storeAvg = False
|
storeAvg = False
|
||||||
if isSpike:
|
|
||||||
storeMaxNS = False
|
|
||||||
else:
|
|
||||||
if (self._maxNonSpikeSession is not None and
|
|
||||||
self._maxNonSpikeSession.getDuration() > duration):
|
|
||||||
storeMaxNS = False
|
|
||||||
if storeAvg:
|
if storeAvg:
|
||||||
if self._avgSession:
|
if self._avgSession:
|
||||||
self._avgSession.release()
|
self._avgSession.release()
|
||||||
session.acquire()
|
session.acquire()
|
||||||
self._avgSession = session
|
self._avgSession = session
|
||||||
if storeMaxNS:
|
|
||||||
if self._maxNonSpikeSession:
|
|
||||||
self._maxNonSpikeSession.release()
|
|
||||||
session.acquire()
|
|
||||||
self._maxNonSpikeSession = session
|
|
||||||
def getAvgDuration(self):
|
def getAvgDuration(self):
|
||||||
return self._durationAverager.getAverage()
|
return self._durationAverager.getAverage()
|
||||||
def getNumDurationSamples(self):
|
def getNumDurationSamples(self):
|
||||||
@ -92,18 +70,13 @@ class TaskTracker:
|
|||||||
def getAvgSession(self):
|
def getAvgSession(self):
|
||||||
# returns profile session for closest-to-average sample
|
# returns profile session for closest-to-average sample
|
||||||
return self._avgSession
|
return self._avgSession
|
||||||
def getMaxNonSpikeSession(self):
|
|
||||||
# returns profile session for closest-to-average sample
|
|
||||||
return self._maxNonSpikeSession
|
|
||||||
def log(self):
|
def log(self):
|
||||||
if self._avgSession:
|
if self._avgSession:
|
||||||
self.notify.info('task CPU profile (%s):\n'
|
s = 'task CPU profile (%s):\n' % self._namePrefix
|
||||||
'== AVERAGE\n%s\n'
|
s += ('~' * 60) + '\n'
|
||||||
'== LONGEST NON-SPIKE\n%s' % (
|
for sorts in (['cumulative'], ['time'], ['calls']):
|
||||||
self._namePrefix,
|
s += self._avgSession.getResults(sorts=sorts)
|
||||||
self._avgSession.getResults(),
|
self.notify.info(s)
|
||||||
self._maxNonSpikeSession.getResults(),
|
|
||||||
))
|
|
||||||
else:
|
else:
|
||||||
self.notify.info('task CPU profile (%s): no data collected' % self._namePrefix)
|
self.notify.info('task CPU profile (%s): no data collected' % self._namePrefix)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user