Separate module to monitor (subsystem-)performance.

Move some code from the subsystem manager to a separate module.
Add module to evaluate performance and expose statistics to properties.
This commit is contained in:
ThorstenB 2011-11-19 15:24:43 +01:00
parent dd45be518f
commit 27a1c0288b
7 changed files with 243 additions and 148 deletions

View File

@ -13,6 +13,7 @@ set(HEADERS
SGSmplstat.hxx SGSmplstat.hxx
SGWeakPtr.hxx SGWeakPtr.hxx
SGWeakReferenced.hxx SGWeakReferenced.hxx
SGPerfMon.hxx
Singleton.hxx Singleton.hxx
StringTable.hxx StringTable.hxx
callback.hxx callback.hxx
@ -29,6 +30,7 @@ set(SOURCES
SGExpression.cxx SGExpression.cxx
SGSmplhist.cxx SGSmplhist.cxx
SGSmplstat.cxx SGSmplstat.cxx
SGPerfMon.cxx
StringTable.cxx StringTable.cxx
commands.cxx commands.cxx
event_mgr.cxx event_mgr.cxx

View File

@ -0,0 +1,130 @@
// SGPerfMon.cxx -- Performance Monitoring
//
// Written by Thorsten Brehm, started November 2011.
//
// Copyright (C) 2011 Thorsten Brehm - brehmt (at) gmail com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include "SGPerfMon.hxx"
#include <simgear/structure/SGSmplstat.hxx>
#include <stdio.h>
#include <string.h>
#include <string>
#include <simgear/constants.h>
#include <simgear/sg_inlines.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/sound/soundmgr_openal.hxx>
#include <simgear/structure/exception.hxx>
using std::string;
SGPerformanceMonitor::SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root) :
_isEnabled(false),
_count(0)
{
_root = root;
_subSysMgr = subSysMgr;
}
void
SGPerformanceMonitor::bind(void)
{
_statiticsSubsystems = _root->getChild("subsystems", 0, true);
_statisticsFlag = _root->getChild("enabled", 0, true);
_statisticsInterval = _root->getChild("interval-s", 0, true);
// _statiticsMinJitter = _root->getChild("min-jitter-ms", 0, true);
// _statiticsMinTime = _root->getChild("min-time-ms", 0, true);
}
void
SGPerformanceMonitor::unbind(void)
{
_statiticsSubsystems = 0;
_statisticsFlag = 0;
_statisticsInterval = 0;
// _statiticsMinJitter = 0;
// _statiticsMinTime = 0;
}
void
SGPerformanceMonitor::init(void)
{
}
void
SGPerformanceMonitor::update(double dt)
{
if (_isEnabled != _statisticsFlag->getBoolValue())
{
// flag has changed, update subsystem manager
_isEnabled = _statisticsFlag->getBoolValue();
if (_isEnabled)
_subSysMgr->setReportTimingCb(this,&subSystemMgrHook);
else
_subSysMgr->setReportTimingCb(this,0);
}
if (!_isEnabled)
return;
if (_lastUpdate.elapsedMSec() > 1000 * _statisticsInterval->getDoubleValue())
{
_count = 0;
// grab timing statistics
_subSysMgr->reportTiming();
_lastUpdate.stamp();
}
}
/** Callback hooked into the subsystem manager. */
void
SGPerformanceMonitor::subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat)
{
((SGPerformanceMonitor*) userData)->reportTiming(name, timeStat);
}
/** Grabs and exposes timing information to properties */
void
SGPerformanceMonitor::reportTiming(const string& name, SampleStatistic* timeStat)
{
SGPropertyNode* node = _statiticsSubsystems->getChild("subsystem",_count++,true);
double minMs = timeStat->min() / 1000;
double maxMs = timeStat->max() / 1000;
double meanMs = timeStat->mean() / 1000;
double stdDevMs = timeStat->stdDev() / 1000;
double totalMs = timeStat->total() / 1000;
int samples = timeStat->samples();
node->setStringValue("name", name);
node->setDoubleValue("min-ms", minMs);
node->setDoubleValue("max-ms", maxMs);
node->setDoubleValue("mean-ms", meanMs);
node->setDoubleValue("stddev-ms", stdDevMs);
node->setDoubleValue("total-ms", totalMs);
node->setDoubleValue("count",samples);
timeStat->reset();
}

View File

@ -0,0 +1,64 @@
// SGPerfMon.hxx -- Performance Tracing
//
// Written by Thorsten Brehm, started November 2011.
//
// Copyright (C) 2011 Thorsten Brehm - brehmt (at) gmail com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef __SGPERFMON_HXX
#define __SGPERFMON_HXX
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/timing/timestamp.hxx>
class SampleStatistic;
///////////////////////////////////////////////////////////////////////////////
// SG Performance Monitor ////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class SGPerformanceMonitor : public SGSubsystem
{
public:
SGPerformanceMonitor(SGSubsystemMgr* subSysMgr, SGPropertyNode_ptr root);
virtual void bind (void);
virtual void unbind (void);
virtual void init (void);
virtual void update (double dt);
private:
static void subSystemMgrHook(void* userData, const std::string& name, SampleStatistic* timeStat);
void reportTiming(const std::string& name, SampleStatistic* timeStat);
SGTimeStamp _lastUpdate;
SGSubsystemMgr* _subSysMgr;
SGPropertyNode_ptr _root;
SGPropertyNode_ptr _statiticsSubsystems;
SGPropertyNode_ptr _statisticsFlag;
SGPropertyNode_ptr _statisticsInterval;
// SGPropertyNode_ptr _statiticsMinJitter;
// SGPropertyNode_ptr _statiticsMinTime;
bool _isEnabled;
int _count;
};
#endif // __SGPERFMON_HXX

View File

@ -81,6 +81,7 @@ void SampleStatistic::operator += (double value)
{ {
n += 1; n += 1;
x += value; x += value;
allTimeTotal += value;
x2 += (value * value); x2 += (value * value);
if (minValue > value) if (minValue > value)
minValue = value; minValue = value;

View File

@ -30,6 +30,7 @@ protected:
double x; double x;
double x2; double x2;
double minValue, maxValue; double minValue, maxValue;
double allTimeTotal;
public: SampleStatistic (); public: SampleStatistic ();
inline virtual ~ SampleStatistic (); inline virtual ~ SampleStatistic ();
@ -42,6 +43,7 @@ public: SampleStatistic ();
double var () const; double var () const;
double min () const; double min () const;
double max () const; double max () const;
double total () const;
double confidence (int p_percentage) const; double confidence (int p_percentage) const;
double confidence (double p_value) const; double confidence (double p_value) const;
@ -58,6 +60,7 @@ public: SampleStatistic ();
inline SampleStatistic::SampleStatistic () inline SampleStatistic::SampleStatistic ()
{ {
allTimeTotal = 0;
reset (); reset ();
} }
inline int SampleStatistic::samples () const inline int SampleStatistic::samples () const
@ -72,6 +75,10 @@ inline double SampleStatistic::max () const
{ {
return (maxValue); return (maxValue);
} }
inline double SampleStatistic::total () const
{
return (allTimeTotal);
}
inline SampleStatistic::~SampleStatistic () inline SampleStatistic::~SampleStatistic ()
{ {

View File

@ -31,11 +31,14 @@
#include <simgear/math/SGMath.hxx> #include <simgear/math/SGMath.hxx>
#include "SGSmplstat.hxx" #include "SGSmplstat.hxx"
const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4; const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of SGSubsystem // Implementation of SGSubsystem
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
SGSubsystemTimingCb SGSubsystem::reportTimingCb = NULL;
void* SGSubsystem::reportTimingUserData = NULL;
SGSubsystem::SGSubsystem () SGSubsystem::SGSubsystem ()
: _suspended(false) : _suspended(false)
@ -100,28 +103,6 @@ SGSubsystem::is_suspended () const
return _suspended; return _suspended;
} }
void
SGSubsystem::printTimingInformation ()
{
SGTimeStamp startTime;
for ( eventTimeVecIterator i = timingInfo.begin();
i != timingInfo.end();
++i) {
if (i == timingInfo.begin()) {
startTime = i->getTime();
} else {
SGTimeStamp endTime = i->getTime();
SG_LOG(SG_GENERAL, SG_ALERT, "- Getting to timestamp : "
<< i->getName() << " takes " << endTime - startTime
<< " sec.");
startTime = endTime;
}
}
}
void SGSubsystem::stamp(const string& name) void SGSubsystem::stamp(const string& name)
{ {
timingInfo.push_back(TimingInfo(name, SGTimeStamp::now())); timingInfo.push_back(TimingInfo(name, SGTimeStamp::now()));
@ -141,11 +122,9 @@ public:
virtual ~Member (); virtual ~Member ();
virtual void update (double delta_time_sec); virtual void update (double delta_time_sec);
void printTimingInformation(double time);
void printTimingStatistics(double minMaxTime=0.0,double minJitter=0.0); void reportTiming(void) { if (reportTimingCb) reportTimingCb(reportTimingUserData, name, &timeStat); }
void updateExecutionTime(double time); void updateExecutionTime(double time) { timeStat += time;}
double getTimeWarningThreshold();
void collectDebugTiming (bool collect) { collectTimeStats = collect; };
SampleStatistic timeStat; SampleStatistic timeStat;
std::string name; std::string name;
@ -166,8 +145,6 @@ SGSubsystemGroup::SGSubsystemGroup () :
SGSubsystemGroup::~SGSubsystemGroup () SGSubsystemGroup::~SGSubsystemGroup ()
{ {
printTimingStatistics();
// reverse order to prevent order dependency problems // reverse order to prevent order dependency problems
for (unsigned int i = _members.size(); i > 0; i--) for (unsigned int i = _members.size(); i > 0; i--)
{ {
@ -233,38 +210,31 @@ SGSubsystemGroup::update (double delta_time_sec)
delta_time_sec = _fixedUpdateTime; delta_time_sec = _fixedUpdateTime;
} }
SGTimeStamp timeStamp;
while (loopCount-- > 0) { while (loopCount-- > 0) {
for (unsigned int i = 0; i < _members.size(); i++) for (unsigned int i = 0; i < _members.size(); i++)
{ {
SGTimeStamp timeStamp = SGTimeStamp::now(); bool recordTime = (reportTimingCb != NULL);
_members[i]->update(delta_time_sec); // indirect call if (recordTime)
timeStamp = SGTimeStamp::now() - timeStamp; timeStamp = SGTimeStamp::now();
double b = timeStamp.toUSecs();
_members[i]->updateExecutionTime(b); _members[i]->update(delta_time_sec); // indirect call
double threshold = _members[i]->getTimeWarningThreshold();
if (( b > threshold ) && (b > 10000)) { if ((recordTime)&&(reportTimingCb))
_members[i]->printTimingInformation(b); {
} timeStamp = SGTimeStamp::now() - timeStamp;
_members[i]->updateExecutionTime(timeStamp.toUSecs());
}
} }
} // of multiple update loop } // of multiple update loop
} }
void void
SGSubsystemGroup::collectDebugTiming(bool collect) SGSubsystemGroup::reportTiming(void)
{
for (unsigned int i = 0; i < _members.size(); i++)
{
_members[i]->collectDebugTiming(collect);
}
}
void
SGSubsystemGroup::printTimingStatistics(double minMaxTime,double minJitter)
{ {
for (unsigned int i = _members.size(); i > 0; i--) for (unsigned int i = _members.size(); i > 0; i--)
{ {
_members[i-1]->printTimingStatistics(minMaxTime, minJitter); _members[i-1]->reportTiming();
_members[i-1]->timeStat.reset();
} }
} }
@ -327,35 +297,6 @@ SGSubsystemGroup::set_fixed_update_time(double dt)
_fixedUpdateTime = dt; _fixedUpdateTime = dt;
} }
/**
* Print timing statistics.
* Only show data if jitter exceeds minJitter or
* maximum time exceeds minMaxTime.
*/
void
SGSubsystemGroup::Member::printTimingStatistics(double minMaxTime,double minJitter)
{
if (collectTimeStats) {
double minTime = timeStat.min() / 1000;
double maxTime = timeStat.max() / 1000;
double meanTime = timeStat.mean() / 1000;
double stddev = timeStat.stdDev() / 1000;
if ((maxTime - minTime >= minJitter)||
(maxTime >= minMaxTime))
{
char buffer[256];
snprintf(buffer, 256, "Timing summary for %20s.\n"
"- mean time: %04.2f ms.\n"
"- min time : %04.2f ms.\n"
"- max time : %04.2f ms.\n"
"- stddev : %04.2f ms.\n", name.c_str(), meanTime, minTime, maxTime, stddev);
SG_LOG(SG_GENERAL, SG_ALERT, buffer);
}
}
}
bool bool
SGSubsystemGroup::has_subsystem (const string &name) const SGSubsystemGroup::has_subsystem (const string &name) const
{ {
@ -390,7 +331,6 @@ SGSubsystemGroup::Member::Member ()
subsystem(0), subsystem(0),
min_step_sec(0), min_step_sec(0),
elapsed_sec(0), elapsed_sec(0),
collectTimeStats(false),
exceptionCount(0) exceptionCount(0)
{ {
} }
@ -433,31 +373,6 @@ SGSubsystemGroup::Member::update (double delta_time_sec)
} }
void
SGSubsystemGroup::Member::printTimingInformation(double time)
{
if (collectTimeStats) {
SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem Timing Alert, subsystem \"" << name << "\": " << time/1000.0 << "ms");
subsystem->printTimingInformation();
}
}
double SGSubsystemGroup::Member::getTimeWarningThreshold()
{
return (timeStat.mean() + 3 * timeStat.stdDev());
}
void SGSubsystemGroup::Member::updateExecutionTime(double time)
{
if (collectTimeStats) {
timeStat += time;
}
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of SGSubsystemMgr. // Implementation of SGSubsystemMgr.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@ -534,14 +449,6 @@ SGSubsystemMgr::update (double delta_time_sec)
} }
} }
void
SGSubsystemMgr::collectDebugTiming(bool collect)
{
for (int i = 0; i < MAX_GROUPS; i++) {
_groups[i]->collectDebugTiming(collect);
}
}
void void
SGSubsystemMgr::suspend () SGSubsystemMgr::suspend ()
{ {
@ -616,11 +523,12 @@ SGSubsystemMgr::get_subsystem (const string &name) const
return s->second; return s->second;
} }
/** Trigger the timing callback to report data for all subsystems. */
void void
SGSubsystemMgr::printTimingStatistics(double minMaxTime,double minJitter) SGSubsystemMgr::reportTiming()
{ {
for (int i = 0; i < MAX_GROUPS; i++) { for (int i = 0; i < MAX_GROUPS; i++) {
_groups[i]->printTimingStatistics(minMaxTime, minJitter); _groups[i]->reportTiming();
} // of groups iteration } // of groups iteration
} }

View File

@ -48,9 +48,12 @@ public:
const SGTimeStamp& getTime() const { return time; } const SGTimeStamp& getTime() const { return time; }
}; };
class SampleStatistic;
typedef std::vector<TimingInfo> eventTimeVec; typedef std::vector<TimingInfo> eventTimeVec;
typedef std::vector<TimingInfo>::iterator eventTimeVecIterator; typedef std::vector<TimingInfo>::iterator eventTimeVecIterator;
typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, SampleStatistic* pStatistic);
/** /**
@ -227,7 +230,7 @@ public:
/** /**
* Suspend or resum operation of this subsystem. * Suspend or resume operation of this subsystem.
* *
* @param suspended true if the subsystem should be suspended, false * @param suspended true if the subsystem should be suspended, false
* otherwise. * otherwise.
@ -253,27 +256,10 @@ public:
*/ */
virtual bool is_suspended () const; virtual bool is_suspended () const;
/** /**
* Keep track of execution time. * Trigger the callback to report timing information for all subsystems.
*
* <p>This method keeps track of timing statistics for each subsystem.</p>
*
* @param time execution time in ms of last call.
*/ */
void updateExecutionTime(double time); void reportTiming(void);
/**
* Print details of execution time.
*
* <p>For debugging purposes, developers can place stamp() calls
* at strategic points in the update() function of each subsystem, which
* record the time between the successive calls to stamp. This method,
* printExecutionTime() is called after exectution of the subsystem
* update function itself to conduct a post-hoc analysis of excecution
* time</p>
*/
void printTimingInformation();
/** /**
* Place time stamps at strategic points in the execution of subsystems * Place time stamps at strategic points in the execution of subsystems
@ -281,15 +267,14 @@ public:
*/ */
void stamp(const std::string& name); void stamp(const std::string& name);
protected: protected:
bool _suspended; bool _suspended;
eventTimeVec timingInfo; eventTimeVec timingInfo;
//int test;
static SGSubsystemTimingCb reportTimingCb;
static void* reportTimingUserData;
}; };
@ -322,8 +307,7 @@ public:
virtual void remove_subsystem (const std::string &name); virtual void remove_subsystem (const std::string &name);
virtual bool has_subsystem (const std::string &name) const; virtual bool has_subsystem (const std::string &name) const;
void collectDebugTiming(bool collect); void reportTiming(void);
void printTimingStatistics(double minMaxTime=0.0,double minJitter=0.0);
/** /**
* *
@ -404,8 +388,8 @@ public:
virtual SGSubsystem * get_subsystem(const std::string &name) const; virtual SGSubsystem * get_subsystem(const std::string &name) const;
void collectDebugTiming(bool collect); void reportTiming();
void printTimingStatistics(double minMaxTime=0.0,double minJitter=0.0); void setReportTimingCb(void* userData,SGSubsystemTimingCb cb) {reportTimingCb = cb;reportTimingUserData = userData;}
private: private:
@ -413,7 +397,6 @@ private:
typedef std::map<std::string, SGSubsystem*> SubsystemDict; typedef std::map<std::string, SGSubsystem*> SubsystemDict;
SubsystemDict _subsystem_map; SubsystemDict _subsystem_map;
}; };