hierarchical_timer.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright, 2000, 2001 Nevrax Ltd.
00006  *
00007  * This file is part of NEVRAX NeL Network Services.
00008  * NEVRAX NeL Network Services is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2, or (at your option)
00011  * any later version.
00012 
00013  * NEVRAX NeL Network Services is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * General Public License for more details.
00017 
00018  * You should have received a copy of the GNU General Public License
00019  * along with NEVRAX NeL Network Services; see the file COPYING. If not, write to the
00020  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00021  * MA 02111-1307, USA.
00022  */
00023 
00024 #include "stdmisc.h"
00025 
00026 #include "nel/misc/hierarchical_timer.h"
00027 #include "nel/misc/common.h"
00028 #include "nel/misc/debug.h"
00029 #include "nel/misc/command.h"
00030 #include "nel/misc/system_info.h"
00031 
00032 #ifdef NL_CPU_INTEL
00033 #include "nel/misc/time_nl.h"
00034 #endif
00035 
00036 #include <map>
00037 
00038 namespace NLMISC
00039 {
00040 
00041 bool   CSimpleClock::_InitDone = false;
00042 uint64 CSimpleClock::_StartStopNumTicks = 0;
00043 
00044 
00045 // root node for all execution paths
00046 CHTimer::CNode  CHTimer::_RootNode;
00047 CHTimer::CNode *CHTimer::_CurrNode = &_RootNode;
00048 CSimpleClock    CHTimer::_PreambuleClock;
00049 CHTimer         CHTimer::_RootTimer("root", true);
00050 bool            CHTimer::_Benching = false;
00051 bool            CHTimer::_BenchStartedOnce = false;
00052 double          CHTimer::_MsPerTick;
00053 bool            CHTimer::_WantStandardDeviation = false;
00054 CHTimer        *CHTimer::_CurrTimer = &_RootTimer;
00055 sint64          CHTimer::_AfterStopEstimateTime= 0;
00056 bool            CHTimer::_AfterStopEstimateTimeDone= false;
00057 
00058 
00059 
00060 
00061 
00062 
00063 
00064 //=================================================================
00065 void CSimpleClock::init()
00066 {
00067     if (_InitDone) return;
00068     const uint numSamples = 10000;
00069 
00070     CSimpleClock observedClock;
00071     CSimpleClock measuringClock;
00072 
00073     measuringClock.start();
00074     for(uint l = 0; l < numSamples; ++l)
00075     {
00076         observedClock.start();
00077         observedClock.stop();
00078     }
00079     measuringClock.stop();
00080 
00081     _StartStopNumTicks = (measuringClock.getNumTicks() >> 1) / numSamples;
00082     _InitDone = true;
00083 }
00084 
00085 
00086 
00087 //=================================================================
00090 /*static void PerformStatistics(const std::vector<double> &values, double &standardDeviation)
00091 {
00092     nlassert(!values.empty());
00093     double total = 0;
00094     double variance = 0;
00095     uint k;
00096     for(k = 0; k < values.size(); ++k)
00097     {
00098         total += (double) values[k];
00099     }
00100     meanValue = total / values.size();
00101     if (values.size() <= 1)
00102     {
00103         standardDeviation = 0.f;
00104         return;
00105     }
00106     for(k = 0; k < values.size(); ++k)
00107     {
00108         variance += NLMISC::sqr((values[k] - meanValue));
00109     }
00110     standardDeviation = ::sqrt(variance / values.size() - 1);
00111 }*/
00112 
00113 
00114 
00115 //=================================================================
00116 CHTimer::CNode::~CNode()
00117 {
00118     releaseSons();
00119     for(uint k = 0; k < Sons.size(); ++k)
00120         delete Sons[k];
00121 }
00122 
00123 //=================================================================
00124 void CHTimer::CNode::releaseSons()
00125 {
00126     for(uint k = 0; k < Sons.size(); ++k)
00127         delete Sons[k];
00128     Sons.clear();
00129 }
00130 
00131 //=================================================================
00132 void CHTimer::CNode::displayPath(CLog *log) const
00133 {
00134     std::string path;
00135     getPath(path);
00136     log->displayRawNL(("HTIMER: " + path).c_str());
00137 }
00138 
00139 //=================================================================
00140 void CHTimer::CNode::getPath(std::string &path) const
00141 {
00142     path.clear();
00143     const CNode *currNode = this;
00144     do
00145     {
00146         path = path.empty() ? currNode->Owner->getName()
00147             : currNode->Owner->getName() + std::string("::") + path;
00148         currNode = currNode->Parent;
00149     }
00150     while (currNode);
00151 }
00152 
00153 
00154 //=================================================================
00155 uint CHTimer::CNode::getNumNodes() const
00156 {
00157     uint sum = 1;
00158     for(uint k = 0; k < Sons.size(); ++k)
00159     {
00160         sum += Sons[k]->getNumNodes();
00161     }
00162     return sum;
00163 }
00164 
00165 
00166 //=================================================================
00167 void CHTimer::walkTreeToCurrent()
00168 {
00169     if (_IsRoot) return;
00170     bool found = false;
00171     for(uint k = 0; k < _CurrNode->Sons.size(); ++k)
00172     {
00173         if (_CurrNode->Sons[k]->Owner == this)
00174         {
00175             _CurrNode = _CurrNode->Sons[k];
00176             found = true;
00177             break;
00178         }
00179     }
00180     if (!found)
00181     {
00182         // no node for this execution path : create a new one
00183         _CurrNode->Sons.push_back(new CNode(this, _CurrNode));
00184         _CurrNode->Sons.back()->Parent = _CurrNode;
00185         _CurrNode = _CurrNode->Sons.back();
00186     }
00187 }
00188 
00189 
00190 
00191 //=================================================================
00192 void    CHTimer::estimateAfterStopTime()
00193 {
00194     if(_AfterStopEstimateTimeDone)
00195         return;
00196     const uint numSamples = 1000;
00197 
00198     // Do as in startBench, reset and init
00199     clear();
00200 
00201     {
00202 #ifdef NL_CPU_INTEL
00203         double freq = (double) CSystemInfo::getProcessorFrequency(false);
00204         _MsPerTick = 1000 / (double) freq;
00205 #else
00206         _MsPerTick = CTime::ticksToSecond(1000);
00207 #endif
00208         CSimpleClock::init();
00209     }
00210 
00211     // start
00212     _Benching = true;
00213     _BenchStartedOnce = true;
00214     _RootNode.Owner = &_RootTimer;
00215     _WantStandardDeviation = false;
00216     _RootTimer.before();
00217 
00218     for(uint i=0;i<numSamples;i++)
00219     {
00220         static NLMISC::CHTimer      estimateSampleTimer("sampleTimer");
00221         estimateSampleTimer.before();
00222         estimateSampleTimer.after();
00223     }
00224 
00225     _RootTimer.after();
00226     _Benching = false;
00227 
00228     // Then the After Stop time is the rootTimer time / numSamples
00229     _AfterStopEstimateTime= (_RootNode.TotalTime-_RootNode.SonsTotalTime) / numSamples;
00230 
00231     _AfterStopEstimateTimeDone= true;
00232 
00233     // must re-clear.
00234     clear();
00235 }
00236 
00237 
00238 //=================================================================
00239 void    CHTimer::startBench(bool wantStandardDeviation /*= false*/, bool quick, bool reset)
00240 {
00241     nlassert(!_Benching);
00242 
00243     // if not done, estimate the AfterStopTime
00244     estimateAfterStopTime();
00245 
00246     if(reset)
00247         clear();
00248 
00249     if(reset)
00250     {
00251 #ifdef NL_CPU_INTEL
00252         double freq = (double) CSystemInfo::getProcessorFrequency(quick);
00253         _MsPerTick = 1000 / (double) freq;
00254 #else
00255         _MsPerTick = CTime::ticksToSecond(1000);
00256 #endif
00257         CSimpleClock::init();
00258     }
00259 
00260     // if reset needed, clearup all
00261     if (reset)
00262         clearSessionStats();
00263 
00264     // clear current for session
00265     clearSessionCurrent();
00266 
00267     // Launch
00268     _Benching = true;
00269     _BenchStartedOnce = true;
00270     _RootNode.Owner = &_RootTimer;
00271     _WantStandardDeviation = wantStandardDeviation;
00272     _RootTimer.before();
00273 }
00274 
00275 //=================================================================
00276 void    CHTimer::endBench()
00277 {
00278     if (!_Benching)
00279         return;
00280 
00281     if (_CurrNode == &_RootNode)
00282     {
00283         _RootTimer.after();
00284     }
00285     else
00286     {
00287         nlwarning("HTIMER: Stopping the bench inside a benched functions !");
00288     }
00289     _Benching = false;
00290 
00291     // spread session stats if root node is greater
00292     updateSessionStats();
00293 }
00294 
00295 //=================================================================
00296 void    CHTimer::display(CLog *log, TSortCriterion criterion, bool displayInline /*= true*/, bool displayEx)
00297 {
00298     CSimpleClock    benchClock;
00299     benchClock.start();
00300     if(!_BenchStartedOnce) // should have done at least one bench
00301     {
00302         benchClock.stop();
00303         _CurrNode->SonsPreambule += benchClock.getNumTicks();
00304         return;
00305     }
00306     log->displayNL("HTIMER: =========================================================================");
00307     log->displayRawNL("HTIMER: Bench cumuled results");
00308     typedef std::map<CHTimer *, TNodeVect> TNodeMap;
00309     TNodeMap nodeMap;
00310     TNodeVect nodeLeft;
00311     nodeLeft.push_back(&_RootNode);
00312 
00314     while (!nodeLeft.empty())
00315     {
00316         CNode *currNode = nodeLeft.back();
00317         nodeMap[currNode->Owner].push_back(currNode);
00318         nodeLeft.pop_back();
00319         nodeLeft.insert(nodeLeft.end(), currNode->Sons.begin(), currNode->Sons.end());
00320     }
00321 
00322     // 2 ) build statistics
00323     typedef std::vector<CTimerStat> TTimerStatVect;
00324     typedef std::vector<CTimerStat *> TTimerStatPtrVect;
00325     TTimerStatVect      stats(nodeMap.size());
00326     TTimerStatPtrVect   statsPtr(stats.size());
00327     //
00328     uint k = 0;
00329     for(TNodeMap::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it)
00330     {
00331         statsPtr[k] = &stats[k];
00332         stats[k].Timer = it->first;
00333         stats[k].buildFromNodes(&(it->second[0]), it->second.size(), _MsPerTick);
00334         ++k;
00335     }
00336 
00337     // 3 ) sort statistics
00338     if (criterion != NoSort)
00339     {
00340         CStatSorter sorter(criterion);
00341         std::sort(statsPtr.begin(), statsPtr.end(), sorter);
00342     }
00343 
00344     // 4 ) get root total time.
00345     CStats  rootStats;
00346     rootStats.buildFromNode( &_RootNode, _MsPerTick);
00347 
00348     // 5 ) display statistics
00349     uint maxNodeLenght = 0;
00350     std::string format;
00351     if (displayInline)
00352     {
00353         for(TTimerStatPtrVect::iterator statIt = statsPtr.begin(); statIt != statsPtr.end(); ++statIt)
00354         {
00355             maxNodeLenght = std::max(maxNodeLenght, (uint)strlen((*statIt)->Timer->_Name));
00356         }
00357         format = "HTIMER: %-" + NLMISC::toString(maxNodeLenght + 1) + "s %s";
00358     }
00359     std::string statsInline;
00360 
00361     log->displayRawNL(format.c_str(), "", " |      total |      local |       visits |  loc%/ glb% | sessn max |       min |       max |      mean");
00362 
00363     for(TTimerStatPtrVect::iterator statIt = statsPtr.begin(); statIt != statsPtr.end(); ++statIt)
00364     {
00365         if (!displayInline)
00366         {
00367             log->displayRawNL("HTIMER: =================================");
00368             log->displayRawNL("HTIMER: Node %s", (*statIt)->Timer->_Name);
00369             (*statIt)->display(log, displayEx, _WantStandardDeviation);
00370         }
00371         else
00372         {
00373             (*statIt)->getStats(statsInline, displayEx, rootStats.TotalTime, _WantStandardDeviation);
00374             char out[4096];
00375             NLMISC::smprintf(out, 2048, format.c_str(), (*statIt)->Timer->_Name, statsInline.c_str());
00376             log->displayRawNL(out);
00377         }
00378     }
00379     benchClock.stop();
00380     _CurrNode->SonsPreambule += benchClock.getNumTicks();
00381 }
00382 
00383 //================================================================================================
00384 void        CHTimer::displayByExecutionPath(CLog *log, TSortCriterion criterion, bool displayInline, bool alignPaths, bool displayEx)
00385 {
00386     CSimpleClock    benchClock;
00387     benchClock.start();
00388     log->displayRawNL("HTIMER: =========================================================================");
00389     log->displayRawNL("HTIMER: Bench by execution path");
00390     nlassert(_BenchStartedOnce); // should have done at least one bench
00391     //
00392     typedef std::vector<CNodeStat>   TNodeStatVect;
00393     typedef std::vector<CNodeStat *> TNodeStatPtrVect;
00394 
00395     TNodeStatVect nodeStats;
00396     nodeStats.reserve(_RootNode.getNumNodes());
00397     TNodeVect nodeLeft;
00398     nodeLeft.push_back(&_RootNode);
00400     while (!nodeLeft.empty())
00401     {
00402         CNode *currNode = nodeLeft.back();
00403 
00404         nodeStats.push_back(CNodeStat());
00405         nodeStats.back().buildFromNode(currNode, _MsPerTick);
00406         nodeStats.back().Node = currNode;
00407 
00408         nodeLeft.pop_back();
00409         nodeLeft.insert(nodeLeft.end(), currNode->Sons.begin(), currNode->Sons.end());
00410 
00411     }
00412 
00414     // create a pointer list
00415     TNodeStatPtrVect nodeStatsPtrs(nodeStats.size());
00416     for(uint k = 0; k < nodeStats.size(); ++k)
00417     {
00418         nodeStatsPtrs[k] = &nodeStats[k];
00419     }
00420 
00421     // 3 ) sort statistics
00422     if (criterion != NoSort)
00423     {
00424         CStatSorter sorter(criterion);
00425         std::sort(nodeStatsPtrs.begin(), nodeStatsPtrs.end(), sorter);
00426     }
00427 
00428     // 4 ) get root total time.
00429     CStats  rootStats;
00430     rootStats.buildFromNode(&_RootNode, _MsPerTick);
00431 
00432     // 5 ) display statistics
00433     std::string statsInline;
00434     std::string nodePath;
00435 
00436     std::string format;
00437     if (displayInline)
00438     {
00439         if (alignPaths)
00440         {
00441             uint maxSize = 0;
00442             std::string np;
00443             for(TNodeStatPtrVect::iterator it = nodeStatsPtrs.begin(); it != nodeStatsPtrs.end(); ++it)
00444             {
00445                 (*it)->Node->getPath(np);
00446                 maxSize = std::max(maxSize, (uint)np.size());
00447             }
00448             format = "HTIMER: %-" + NLMISC::toString(maxSize) +"s %s";
00449         }
00450         else
00451         {
00452             format = "HTIMER: %s %s";
00453         }
00454     }
00455 
00456     log->displayRawNL(format.c_str(), "", " |      total |      local |       visits |  loc%/ glb% | sessn max |       min |       max |      mean");
00457 
00458     for(TNodeStatPtrVect::iterator it = nodeStatsPtrs.begin(); it != nodeStatsPtrs.end(); ++it)
00459     {
00460         if (!displayInline)
00461         {
00462             log->displayRawNL("HTIMER: =================================");
00463             (*it)->Node->displayPath(log);
00464             (*it)->display(log, displayEx, _WantStandardDeviation);
00465         }
00466         else
00467         {
00468             (*it)->getStats(statsInline, displayEx, rootStats.TotalTime, _WantStandardDeviation);
00469             (*it)->Node->getPath(nodePath);
00470 
00471             char out[2048];
00472             NLMISC::smprintf(out, 2048, format.c_str(), nodePath.c_str(), statsInline.c_str());
00473             log->displayRawNL(out);
00474         }
00475     }
00476     benchClock.stop();
00477     _CurrNode->SonsPreambule += benchClock.getNumTicks();
00478 }
00479 
00480 //=================================================================
00481 /*static*/ void CHTimer::displayHierarchical(CLog *log, bool displayEx /*=true*/,uint labelNumChar /*=32*/, uint indentationStep /*= 2*/)
00482 {
00483     CSimpleClock    benchClock;
00484     benchClock.start();
00485     log->displayNL("HTIMER: =========================================================================");
00486     log->displayRawNL("HTIMER: Hierarchical display of bench");
00487     nlassert(_BenchStartedOnce); // should have done at least one bench
00488     typedef std::map<CHTimer *, TNodeVect> TNodeMap;
00489     TNodeMap nodeMap;
00490     TNodeVect nodeLeft;
00491     nodeLeft.push_back(&_RootNode);
00493     while (!nodeLeft.empty())
00494     {
00495         CNode *currNode = nodeLeft.back();
00496         nodeMap[currNode->Owner].push_back(currNode);
00497         nodeLeft.pop_back();
00498         nodeLeft.insert(nodeLeft.end(), currNode->Sons.begin(), currNode->Sons.end());
00499 
00500     }
00501     log->displayRawNL("HTIMER: %*s |      total |      local |       visits |  loc%%/ glb%% | sessn max |       min |       max |      mean", labelNumChar, "");
00502 
00504     CStats  rootStats;
00505     rootStats.buildFromNode(&_RootNode, _MsPerTick);
00506 
00508     CStats  currNodeStats;
00509     std::vector<uint> sonsIndex;
00510     uint depth = 0;
00511     CHTimer *currTimer = &_RootTimer;
00512     sonsIndex.push_back(0);
00513     bool displayStat = true;
00514     std::string resultName;
00515     std::string resultStats;
00516     while (!sonsIndex.empty())
00517     {
00518         if (displayStat)
00519         {
00520             resultName.resize(labelNumChar);
00521             std::fill(resultName.begin(), resultName.end(), '.');
00522             uint startIndex = depth * indentationStep;
00523             uint endIndex = std::min(startIndex + (uint)::strlen(currTimer->_Name), labelNumChar);
00524             if ((sint) (endIndex - startIndex) >= 1)
00525             {
00526                 std::copy(currTimer->_Name, currTimer->_Name + (endIndex - startIndex), resultName.begin() + startIndex);
00527             }
00528             TNodeVect &execNodes = nodeMap[currTimer];
00529             if (execNodes.size() > 0)
00530             {
00531                 currNodeStats.buildFromNodes(&execNodes[0], execNodes.size(), _MsPerTick);
00532                 currNodeStats.getStats(resultStats, displayEx, rootStats.TotalTime, _WantStandardDeviation);
00533                 log->displayRawNL("HTIMER: %s", (resultName + resultStats).c_str());
00534             }
00535         }
00536         if (sonsIndex.back() == currTimer->_Sons.size())
00537         {
00538             sonsIndex.pop_back();
00539             currTimer = currTimer->_Parent;
00540             displayStat = false;
00541             -- depth;
00542         }
00543         else
00544         {
00545             currTimer = currTimer->_Sons[sonsIndex.back()];
00546             ++ sonsIndex.back();
00547             sonsIndex.push_back(0);
00548             displayStat = true;
00549             ++ depth;
00550         }
00551     }
00552     benchClock.stop();
00553     _CurrNode->SonsPreambule += benchClock.getNumTicks();
00554 }
00555 
00556 
00557 //=================================================================
00558 /*static*/ void     CHTimer::displayHierarchicalByExecutionPath(CLog *log, bool displayEx, uint labelNumChar, uint indentationStep)
00559 {
00560     displayHierarchicalByExecutionPathSorted(log, NoSort, displayEx, labelNumChar, indentationStep);
00561 }
00562 
00563 
00564 //=================================================================
00565 /*static*/ void     CHTimer::displayHierarchicalByExecutionPathSorted(CLog *log, TSortCriterion criterion, bool displayEx, uint labelNumChar, uint indentationStep)
00566 {
00567 
00568     CSimpleClock    benchClock;
00569     benchClock.start();
00570     nlassert(_BenchStartedOnce); // should have done at least one bench
00571 
00572     // get root total time.
00573     CStats  rootStats;
00574     rootStats.buildFromNode(&_RootNode, _MsPerTick);
00575 
00576 
00577     // display header.
00578     CLog::TDisplayInfo  dummyDspInfo;
00579     log->displayRawNL("HTIMER: =========================================================================");
00580     log->displayRawNL("HTIMER: Hierarchical display of bench by execution path");
00581     log->displayRawNL("HTIMER: %*s |      total |      local |       visits |  loc%%/ glb%% | sessn max |       min |       max |      mean", labelNumChar, "");
00582 
00583 
00584     // use list because vector of vector is bad.
00585     std::list< CExamStackEntry >    examStack;
00586 
00587     // Add the root to the stack.
00588     examStack.push_back( CExamStackEntry( &_RootNode ) );
00589     CStats      currNodeStats;
00590     std::string resultName;
00591     std::string resultStats;
00592 
00593     while (!examStack.empty())
00594     {
00595         CNode               *node = examStack.back().Node;
00596         std::vector<CNode*> &children= examStack.back().Children;
00597         uint                child = examStack.back().CurrentChild;
00598 
00599         // If child 0, then must first build children info and display me.
00600         if (child == 0)
00601         {
00602             // Build Sons Infos.
00603             // ==============
00604 
00605             // resize array
00606             children.resize(node->Sons.size());
00607 
00608             // If no sort, easy.
00609             if(criterion == NoSort)
00610             {
00611                 children= node->Sons;
00612             }
00613             // else, Sort them with criterion.
00614             else
00615             {
00616                 std::vector<CNodeStat>      stats;
00617                 std::vector<CNodeStat *>    ptrStats;
00618                 stats.resize(children.size());
00619                 ptrStats.resize(children.size());
00620 
00621                 // build stats.
00622                 uint    i;
00623                 for(i=0; i<children.size(); i++)
00624                 {
00625                     CNode   *childNode= node->Sons[i];
00626                     stats[i].buildFromNode(childNode, _MsPerTick);
00627                     stats[i].Node = childNode;
00628                     ptrStats[i]= &stats[i];
00629                 }
00630 
00631                 // sort.
00632                 CStatSorter sorter;
00633                 sorter.Criterion= criterion;
00634                 std::sort(ptrStats.begin(), ptrStats.end(), sorter);
00635 
00636                 // fill children.
00637                 for(i=0; i<children.size(); i++)
00638                 {
00639                     children[i]= ptrStats[i]->Node;
00640                 }
00641             }
00642 
00643 
00644             // Display our infos
00645             // ==============
00646             // build the indented node name.
00647             resultName.resize(labelNumChar);
00648             std::fill(resultName.begin(), resultName.end(), '.');
00649             uint startIndex = (examStack.size()-1) * indentationStep;
00650             uint endIndex = std::min(startIndex + (uint)::strlen(node->Owner->_Name), labelNumChar);
00651             if ((sint) (endIndex - startIndex) >= 1)
00652             {
00653                 std::copy(node->Owner->_Name, node->Owner->_Name + (endIndex - startIndex), resultName.begin() + startIndex);
00654             }
00655 
00656             // build the stats string.
00657             currNodeStats.buildFromNode(node, _MsPerTick);
00658             currNodeStats.getStats(resultStats, displayEx, rootStats.TotalTime, _WantStandardDeviation);
00659 
00660             // display
00661             log->displayRawNL("HTIMER: %s", (resultName + resultStats).c_str());
00662         }
00663 
00664         // End of sons?? stop.
00665         if (child >= children.size())
00666         {
00667             examStack.pop_back();
00668             continue;
00669         }
00670 
00671         // next son.
00672         ++(examStack.back().CurrentChild);
00673 
00674         // process the current son.
00675         examStack.push_back( CExamStackEntry( children[child] ) );
00676     }
00677 
00678     //
00679     benchClock.stop();
00680     _CurrNode->SonsPreambule += benchClock.getNumTicks();
00681 }
00682 
00683 //=================================================================
00684 /*static*/ void     CHTimer::displaySummary(CLog *log, TSortCriterion criterion, bool displayEx, uint labelNumChar, uint indentationStep, uint maxDepth)
00685 {
00686 
00687     CSimpleClock    benchClock;
00688     benchClock.start();
00689     nlassert(_BenchStartedOnce); // should have done at least one bench
00690 
00691     // get root total time.
00692     CStats  rootStats;
00693     rootStats.buildFromNode(&_RootNode, _MsPerTick);
00694 
00695 
00696     // display header.
00697     CLog::TDisplayInfo  dummyDspInfo;
00698     log->displayRawNL("HTIMER: =========================================================================");
00699     log->displayRawNL("HTIMER: Hierarchical display of bench by execution path");
00700     log->displayRawNL("HTIMER: %*s |      total |      local |       visits |  loc%%/ glb%% | sessn max |       min |       max |      mean", labelNumChar, "");
00701 
00702 
00703     // use list because vector of vector is bad.
00704     std::list< CExamStackEntry >    examStack;
00705 
00706     // Add the root to the stack.
00707     examStack.push_back( CExamStackEntry( &_RootNode ) );
00708     CStats      currNodeStats;
00709     std::string resultName;
00710     std::string resultStats;
00711 
00712     while (!examStack.empty())
00713     {
00714         CNode               *node = examStack.back().Node;
00715         std::vector<CNode*> &children= examStack.back().Children;
00716         uint                child = examStack.back().CurrentChild;
00717         uint                depth = examStack.back().Depth;
00718 
00719         // If child 0, then must first build children info and display me.
00720         if (child == 0)
00721         {
00722             // Build Sons Infos.
00723             // ==============
00724 
00725             // resize array
00726             children.resize(node->Sons.size());
00727 
00728             // If no sort, easy.
00729             if(criterion == NoSort)
00730             {
00731                 children= node->Sons;
00732             }
00733             // else, Sort them with criterion.
00734             else
00735             {
00736                 std::vector<CNodeStat>      stats;
00737                 std::vector<CNodeStat *>    ptrStats;
00738                 stats.resize(children.size());
00739                 ptrStats.resize(children.size());
00740 
00741                 // build stats.
00742                 uint    i;
00743                 for(i=0; i<children.size(); i++)
00744                 {
00745                     CNode   *childNode= node->Sons[i];
00746                     stats[i].buildFromNode(childNode, _MsPerTick);
00747                     stats[i].Node = childNode;
00748                     ptrStats[i]= &stats[i];
00749                 }
00750 
00751                 // sort.
00752                 CStatSorter sorter;
00753                 sorter.Criterion= criterion;
00754                 std::sort(ptrStats.begin(), ptrStats.end(), sorter);
00755 
00756                 // fill children.
00757                 for(i=0; i<children.size(); i++)
00758                 {
00759                     children[i]= ptrStats[i]->Node;
00760                 }
00761             }
00762 
00763 
00764             // Display our infos
00765             // ==============
00766             // build the indented node name.
00767             resultName.resize(labelNumChar);
00768             std::fill(resultName.begin(), resultName.end(), '.');
00769             uint startIndex = (examStack.size()-1) * indentationStep;
00770             uint endIndex = std::min(startIndex + (uint)::strlen(node->Owner->_Name), labelNumChar);
00771             if ((sint) (endIndex - startIndex) >= 1)
00772             {
00773                 std::copy(node->Owner->_Name, node->Owner->_Name + (endIndex - startIndex), resultName.begin() + startIndex);
00774             }
00775 
00776             // build the stats string.
00777             currNodeStats.buildFromNode(node, _MsPerTick);
00778             currNodeStats.getStats(resultStats, displayEx, rootStats.TotalTime, _WantStandardDeviation);
00779 
00780             // display
00781             log->displayRawNL("HTIMER: %s", (resultName + resultStats).c_str());
00782         }
00783 
00784         // End of sons?? stop.
00785         if (child >= children.size())
00786         {
00787             examStack.pop_back();
00788             continue;
00789         }
00790 
00791         // next son.
00792         ++(examStack.back().CurrentChild);
00793 
00794         // process the current son.
00795         if (depth+1 < maxDepth)
00796             examStack.push_back( CExamStackEntry( children[child], depth+1 ) );
00797     }
00798 
00799     //
00800     benchClock.stop();
00801     _CurrNode->SonsPreambule += benchClock.getNumTicks();
00802 }
00803 
00804 //=================================================================
00805 void    CHTimer::clear()
00806 {
00807     // should not be benching !
00808     nlassert(_CurrNode == &_RootNode);
00809     _RootNode.releaseSons();
00810     _CurrNode = &_RootNode;
00811     _RootNode.reset();
00812 }
00813 
00814 //=================================================================
00815 void CHTimer::CStats::buildFromNode(CNode *node, double msPerTick)
00816 {
00817     buildFromNodes(&node, 1, msPerTick);
00818 }
00819 
00820 //=================================================================
00821 void CHTimer::CStats::buildFromNodes(CNode **nodes, uint numNodes, double msPerTick)
00822 {
00823     TotalTime = 0;
00824     TotalTimeWithoutSons = 0;
00825     NumVisits = 0;
00826 
00827     uint64 minTime = (uint64) -1;
00828     uint64 maxTime = 0;
00829     uint64 sessionMaxTime = 0;
00830 
00831     uint k, l;
00832     for(k = 0; k < numNodes; ++k)
00833     {
00834         TotalTime += nodes[k]->TotalTime * msPerTick;
00835         TotalTimeWithoutSons += (nodes[k]->TotalTime -  nodes[k]->LastSonsTotalTime) * msPerTick;
00836         NumVisits += nodes[k]->NumVisits;
00837         minTime = std::min(minTime, nodes[k]->MinTime);
00838         maxTime = std::max(maxTime, nodes[k]->MaxTime);
00839         sessionMaxTime = std::max(sessionMaxTime, nodes[k]->SessionMax);
00840     }
00841     if (minTime == (uint64) -1)
00842     {
00843         minTime = 0;
00844     }
00845     MinTime  = minTime * msPerTick;
00846     MaxTime  = maxTime * msPerTick;
00847     SessionMaxTime = sessionMaxTime * msPerTick;
00848 
00849     if (NumVisits > 0)
00850         MeanTime = TotalTime / NumVisits;
00851     else
00852         MeanTime = 0.0;
00853 
00854     // compute standard deviation
00855     double varianceSum = 0;
00856     uint   numMeasures = 0;
00857     for(k = 0; k < numNodes; ++k)
00858     {
00859         numMeasures += nodes[k]->Measures.size();
00860         for(l = 0; l < nodes[k]->Measures.size(); ++l)
00861         {
00862             varianceSum += NLMISC::sqr(nodes[k]->Measures[l] - MeanTime);
00863         }
00864     }
00865     TimeStandardDeviation = numMeasures == 0 ? 0
00866                                              : ::sqrt(varianceSum / (numMeasures +1));
00867 }
00868 
00869 //=================================================================
00870 void CHTimer::CStats::display(CLog *log, bool displayEx, bool wantStandardDeviation /* = false*/)
00871 {
00872     log->displayRawNL("HTIMER: Total time                = %.3f ms", (float) TotalTime);
00873     log->displayRawNL("HTIMER: Total time without sons   = %.3f ms", (float) TotalTimeWithoutSons);
00874     log->displayRawNL(("HTIMER: Num visits                = " + NLMISC::toString(NumVisits)).c_str());
00875     if (displayEx)
00876     {
00877             log->displayRawNL("HTIMER: Min time                  = %.3f ms", (float) MinTime);
00878             log->displayRawNL("HTIMER: Max time                  = %.3f ms", (float) MaxTime);
00879             log->displayRawNL("HTIMER: Mean time                 = %.3f ms", (float) MeanTime);
00880             if (wantStandardDeviation)
00881             {
00882             log->displayRawNL("HTIMER: Standard deviation        = %.3f ms", (float) TimeStandardDeviation);
00883             }
00884             log->displayRawNL("HTIMER: Session Max time          = %.3f ms", (float) SessionMaxTime);
00885             //log->displayRawNL("Time standard deviation    = %.3f ms", (float) TimeStandardDeviation);
00886     }
00887 }
00888 
00889 
00890 //=================================================================
00891 void CHTimer::CStats::getStats(std::string &dest, bool statEx, double rootTotalTime, bool wantStandardDeviation /*= false*/)
00892 {
00893     char buf[1024];
00894     if (!wantStandardDeviation)
00895     {
00896         if (!statEx)
00897         {
00898             NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s ", (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str());
00899         }
00900         else
00901         {
00902             NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s | %5.1f/%5.1f | %9.3f | %9.3f | %9.3f | %9.3f",
00903                       (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str(),
00904                       float(100*TotalTimeWithoutSons/rootTotalTime), float(100*TotalTime/rootTotalTime),
00905                       (float) SessionMaxTime,
00906                       (float) MinTime, (float) MaxTime, (float) MeanTime
00907                      );
00908         }
00909     }
00910     else
00911     {
00912         if (!statEx)
00913         {
00914             NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s | std deviation %9.3f", (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str(), (float) TimeStandardDeviation);
00915         }
00916         else
00917         {
00918             NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s | %5.1f/%5.1f | %9.3f | %9.3f | %9.3f | %9.3f | std deviation %9.3f",
00919                               (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str(),
00920                               float(100*TotalTimeWithoutSons/rootTotalTime), float(100*TotalTime/rootTotalTime),
00921                               (float) SessionMaxTime,
00922                               (float) MinTime, (float) MaxTime, (float) MeanTime,
00923                               (float) TimeStandardDeviation
00924                             );
00925         }
00926     }
00927     dest = buf;
00928 }
00929 
00930 
00931 //=================================================================
00932 bool CHTimer::CStatSorter::operator()(const CHTimer::CStats *lhs, const CHTimer::CStats *rhs)
00933 {
00934     switch(Criterion)
00935     {
00936         case CHTimer::TotalTime:                return lhs->TotalTime >= rhs->TotalTime;
00937         case CHTimer::TotalTimeWithoutSons:     return lhs->TotalTimeWithoutSons >= rhs->TotalTimeWithoutSons;
00938         case CHTimer::MeanTime:                 return lhs->MeanTime >= rhs->MeanTime;
00939         case CHTimer::NumVisits:                return lhs->NumVisits >= rhs->NumVisits;
00940         case CHTimer::MaxTime:                  return lhs->MaxTime >= rhs->MaxTime;
00941         case CHTimer::MinTime:                  return lhs->MinTime < rhs->MinTime;
00942         case CHTimer::MaxSession:               return lhs->SessionMaxTime > rhs->SessionMaxTime;
00943         default:
00944             nlassert(0); // not a valid criterion
00945         break;
00946     }
00947     return false;
00948 }
00949 
00950 
00951 //===============================================
00952 void    CHTimer::doBefore()
00953 {
00954     _PreambuleClock.start();
00955     walkTreeToCurrent();
00956     ++ _CurrNode->NumVisits;
00957     _CurrNode->SonsPreambule = 0;
00958     if (!_Parent && _CurrTimer != this)
00959     {
00960         _Parent = _CurrTimer;
00961         // register as a son of the parent
00962         _Parent->_Sons.push_back(this);
00963     }
00964     _CurrTimer = this;
00965     _PreambuleClock.stop();
00966     if (_CurrNode->Parent)
00967     {
00968         _CurrNode->Parent->SonsPreambule += _PreambuleClock.getNumTicks();
00969     }
00970     _CurrNode->Clock.start();
00971 }
00972 
00973 //===============================================
00974 void    CHTimer::doAfter(bool displayAfter)
00975 {
00976     _CurrNode->Clock.stop();
00977     _PreambuleClock.start();
00978     /* Remove my Son preambule, and remove only ONE StartStop
00979         It is because between the start and the end, only ONE rdtsc time is counted:
00980     */
00981     sint64 numTicks = _CurrNode->Clock.getNumTicks()  - _CurrNode->SonsPreambule - (CSimpleClock::getStartStopNumTicks());
00982     // Case where the SonPreambule is overestimated,
00983     numTicks= std::max((sint64)0, numTicks);
00984     // In case where the SonPreambule is overestimated, the TotalTime must not be < of the SonTime
00985     if(_CurrNode->TotalTime + numTicks < _CurrNode->SonsTotalTime)
00986         numTicks= _CurrNode->SonsTotalTime - _CurrNode->TotalTime;
00987 
00988     _CurrNode->TotalTime += numTicks;
00989     _CurrNode->MinTime = std::min(_CurrNode->MinTime, (uint64)numTicks);
00990     _CurrNode->MaxTime = std::max(_CurrNode->MaxTime, (uint64)numTicks);
00991     _CurrNode->LastSonsTotalTime = _CurrNode->SonsTotalTime;
00992 
00993     _CurrNode->SessionCurrent += (uint64)numTicks;
00994 
00995     if (displayAfter)
00996     {
00997         nlinfo("HTIMER: %s %.3fms loop number %d", _Name, numTicks * _MsPerTick, _CurrNode->NumVisits);
00998     }
00999     //
01000     if (_WantStandardDeviation)
01001     {
01002         _CurrNode->Measures.push_back(numTicks * _MsPerTick);
01003     }
01004     //
01005     if (_Parent)
01006     {
01007         _CurrTimer = _Parent;
01008     }
01009     //
01010     if (_CurrNode->Parent)
01011     {
01012         CNode   *curNode= _CurrNode;
01013         CNode   *parent= _CurrNode->Parent;
01014         parent->SonsTotalTime += numTicks;
01015         _PreambuleClock.stop();
01016         /*
01017             The SonPreambule of my parent is
01018                 + my BeforePreambule (counted in doBefore)
01019                 + my Afterpreambule (see below)
01020                 + my Sons Preambule
01021                 + some constant time due to the Start/Stop of the _CurrNode->Clock, the 2* Start/Stop
01022                     of the PreabmuleClock, the function call time of doBefore and doAfter
01023         */
01024         parent->SonsPreambule += _PreambuleClock.getNumTicks() + curNode->SonsPreambule + _AfterStopEstimateTime;
01025         // walk to parent
01026         _CurrNode= parent;
01027     }
01028     else
01029     {
01030         _PreambuleClock.stop();
01031     }
01032 }
01033 
01034 
01035 
01036 
01037 /*
01038  * Clears SessionMax current stats (only current value)
01039  */
01040 void    CHTimer::clearSessionCurrent()
01041 {
01042     _RootNode.resetSessionCurrent();
01043 }
01044 
01045 /*
01046  * Clears all SessionMax stats (max and current values)
01047  */
01048 void    CHTimer::clearSessionStats()
01049 {
01050     _RootNode.resetSessionStats();
01051 }
01052 
01053 /*
01054  * Update session stats
01055  */
01056 void    CHTimer::updateSessionStats()
01057 {
01058     if (_RootNode.SessionCurrent > _RootNode.SessionMax)
01059         _RootNode.spreadSession();
01060 }
01061 
01062 
01063 
01064 
01065 //
01066 // Commands
01067 //
01068 
01069 #define CASE_DISPLAYMEASURES(crit, alternative) \
01070     if (args[0] == #crit || depth == alternative)   \
01071     {   \
01072         CHTimer::TSortCriterion criterion = CHTimer::crit;  \
01073         if (hasDepth && depth != alternative)   \
01074             CHTimer::displaySummary(&log, criterion, true, 64, 2, depth);   \
01075         else    \
01076             CHTimer::display(&log, criterion);  \
01077     }   \
01078     else
01079 
01080 NLMISC_CATEGORISED_COMMAND(nel,displayMeasures, "display hierarchical timer", "[TotalTime(=-2)|NoSort(=-3)|TotalTimeWithoutSons(=-4)|MeanTime(=-5)|NumVisits(=-6)|MaxTime(=-7)|MinTime(=-8)|MaxSession(=-9)] [depth]")
01081 {
01082     if (args.size() < 1)
01083     {
01084         CHTimer::display(&log);
01085         CHTimer::displayHierarchicalByExecutionPathSorted (&log, CHTimer::TotalTime, true, 64);
01086         return true;
01087     }
01088 
01089     sint    depth = 0;
01090     bool    hasDepth = (sscanf(args[0].c_str(), "%d", &depth) == 1 || (args.size() > 1 && sscanf(args[1].c_str(), "%d", &depth) == 1));
01091 
01092     CASE_DISPLAYMEASURES(NoSort, -3)
01093     CASE_DISPLAYMEASURES(TotalTime, -2)
01094     CASE_DISPLAYMEASURES(TotalTimeWithoutSons, -4)
01095     CASE_DISPLAYMEASURES(MeanTime, -5)
01096     CASE_DISPLAYMEASURES(NumVisits, -6)
01097     CASE_DISPLAYMEASURES(MaxTime, -7)
01098     CASE_DISPLAYMEASURES(MinTime, -8)
01099     CASE_DISPLAYMEASURES(MaxSession, -9)
01100     {
01101         if (hasDepth)
01102             CHTimer::displaySummary(&log, CHTimer::TotalTime, true, 64, 2, depth);
01103         else
01104             CHTimer::display(&log, CHTimer::TotalTime);
01105     }
01106 
01107     return true;
01108 }
01109 
01110 } // NLMISC
01111 

Generated on Thu Jan 7 08:26:34 2010 for NeL by  doxygen 1.6.1