Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / misc / hierarchical_timer.cpp
blob55eba958a703e42737dbc7d3ad9381514a4e7a3f
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
19 #include "nel/misc/hierarchical_timer.h"
20 #include "nel/misc/common.h"
21 #include "nel/misc/debug.h"
22 #include "nel/misc/command.h"
23 #include "nel/misc/system_info.h"
25 #ifdef NL_CPU_INTEL
26 #include "nel/misc/time_nl.h"
27 #endif
29 #ifdef DEBUG_NEW
30 #define new DEBUG_NEW
31 #endif
33 namespace NLMISC
36 bool CSimpleClock::_InitDone = false;
37 uint64 CSimpleClock::_StartStopNumTicks = 0;
40 // root node for all execution paths
41 CHTimer::CNode CHTimer::_RootNode;
42 CHTimer::CNode *CHTimer::_CurrNode = &_RootNode;
43 CSimpleClock CHTimer::_PreambuleClock;
44 CHTimer CHTimer::_RootTimer("root", true);
45 bool CHTimer::_Benching = false;
46 bool CHTimer::_BenchStartedOnce = false;
47 double CHTimer::_MsPerTick;
48 bool CHTimer::_WantStandardDeviation = false;
49 CHTimer *CHTimer::_CurrTimer = &_RootTimer;
50 sint64 CHTimer::_AfterStopEstimateTime= 0;
51 bool CHTimer::_AfterStopEstimateTimeDone= false;
59 //=================================================================
60 void CSimpleClock::init()
62 if (_InitDone) return;
63 const uint numSamples = 10000;
65 CSimpleClock observedClock;
66 CSimpleClock measuringClock;
68 measuringClock.start();
69 for(uint l = 0; l < numSamples; ++l)
71 observedClock.start();
72 observedClock.stop();
74 measuringClock.stop();
76 _StartStopNumTicks = (measuringClock.getNumTicks() >> 1) / numSamples;
77 _InitDone = true;
82 //=================================================================
83 /** Do simple statistics on a list of values (mean value, standard deviation)
85 /*static void PerformStatistics(const std::vector<double> &values, double &standardDeviation)
87 nlassert(!values.empty());
88 double total = 0;
89 double variance = 0;
90 uint k;
91 for(k = 0; k < values.size(); ++k)
93 total += (double) values[k];
95 meanValue = total / values.size();
96 if (values.size() <= 1)
98 standardDeviation = 0.f;
99 return;
101 for(k = 0; k < values.size(); ++k)
103 variance += NLMISC::sqr((values[k] - meanValue));
105 standardDeviation = ::sqrt(variance / values.size() - 1);
110 //=================================================================
111 CHTimer::CNode::~CNode()
113 releaseSons();
114 for(uint k = 0; k < Sons.size(); ++k)
115 delete Sons[k];
118 //=================================================================
119 void CHTimer::CNode::releaseSons()
121 for(uint k = 0; k < Sons.size(); ++k)
122 delete Sons[k];
123 Sons.clear();
126 //=================================================================
127 void CHTimer::CNode::displayPath(CLog *log) const
129 std::string path;
130 getPath(path);
131 log->displayRawNL(("HTIMER: " + path).c_str());
134 //=================================================================
135 void CHTimer::CNode::getPath(std::string &path) const
137 path.clear();
138 const CNode *currNode = this;
141 path = path.empty() ? currNode->Owner->getName()
142 : currNode->Owner->getName() + std::string("::") + path;
143 currNode = currNode->Parent;
145 while (currNode);
149 //=================================================================
150 uint CHTimer::CNode::getNumNodes() const
152 uint sum = 1;
153 for(uint k = 0; k < Sons.size(); ++k)
155 sum += Sons[k]->getNumNodes();
157 return sum;
161 //=================================================================
162 void CHTimer::walkTreeToCurrent()
164 if (_IsRoot) return;
165 bool found = false;
166 for(uint k = 0; k < _CurrNode->Sons.size(); ++k)
168 if (_CurrNode->Sons[k]->Owner == this)
170 _CurrNode = _CurrNode->Sons[k];
171 found = true;
172 break;
175 if (!found)
177 // no node for this execution path : create a new one
178 _CurrNode->Sons.push_back(new CNode(this, _CurrNode));
179 _CurrNode->Sons.back()->Parent = _CurrNode;
180 _CurrNode = _CurrNode->Sons.back();
186 //=================================================================
187 void CHTimer::estimateAfterStopTime()
189 if(_AfterStopEstimateTimeDone)
190 return;
191 const uint numSamples = 1000;
193 // Do as in startBench, reset and init
194 clear();
197 #ifdef NL_CPU_INTEL
198 double freq = (double) CSystemInfo::getProcessorFrequency(false);
199 _MsPerTick = 1000 / (double) freq;
200 #else
201 _MsPerTick = CTime::ticksToSecond(1000);
202 #endif
203 CSimpleClock::init();
206 // start
207 _Benching = true;
208 _BenchStartedOnce = true;
209 _RootNode.Owner = &_RootTimer;
210 _WantStandardDeviation = false;
211 _RootTimer.before();
213 for(uint i=0;i<numSamples;i++)
215 static NLMISC::CHTimer estimateSampleTimer("sampleTimer");
216 estimateSampleTimer.before();
217 estimateSampleTimer.after();
220 _RootTimer.after();
221 _Benching = false;
223 // Then the After Stop time is the rootTimer time / numSamples
224 _AfterStopEstimateTime= (_RootNode.TotalTime-_RootNode.SonsTotalTime) / numSamples;
226 _AfterStopEstimateTimeDone= true;
228 // must re-clear.
229 clear();
233 //=================================================================
234 void CHTimer::startBench(bool wantStandardDeviation /*= false*/, bool quick, bool reset)
236 nlassert(!_Benching);
238 // if not done, estimate the AfterStopTime
239 estimateAfterStopTime();
241 if(reset)
242 clear();
244 if(reset)
246 #ifdef NL_CPU_INTEL
247 double freq = (double) CSystemInfo::getProcessorFrequency(quick);
248 _MsPerTick = 1000 / (double) freq;
249 #else
250 _MsPerTick = CTime::ticksToSecond(1000);
251 #endif
252 CSimpleClock::init();
255 // if reset needed, clearup all
256 if (reset)
257 clearSessionStats();
259 // clear current for session
260 clearSessionCurrent();
262 // Launch
263 _Benching = true;
264 _BenchStartedOnce = true;
265 _RootNode.Owner = &_RootTimer;
266 _WantStandardDeviation = wantStandardDeviation;
267 _RootTimer.before();
270 //=================================================================
271 void CHTimer::endBench()
273 if (!_Benching)
274 return;
276 if (_CurrNode == &_RootNode)
278 _RootTimer.after();
280 else
282 nlwarning("HTIMER: Stopping the bench inside a benched functions !");
284 _Benching = false;
286 // spread session stats if root node is greater
287 updateSessionStats();
290 //=================================================================
291 void CHTimer::display(CLog *log, TSortCriterion criterion, bool displayInline /*= true*/, bool displayEx)
293 CSimpleClock benchClock;
294 benchClock.start();
295 if(!_BenchStartedOnce) // should have done at least one bench
297 benchClock.stop();
298 _CurrNode->SonsPreambule += benchClock.getNumTicks();
299 return;
301 log->displayNL("HTIMER: =========================================================================");
302 log->displayRawNL("HTIMER: Bench cumuled results");
303 typedef std::map<CHTimer *, TNodeVect> TNodeMap;
304 TNodeMap nodeMap;
305 TNodeVect nodeLeft;
306 nodeLeft.push_back(&_RootNode);
308 /// 1 ) walk the tree to build the node map (well, in a not very optimal way..)
309 while (!nodeLeft.empty())
311 CNode *currNode = nodeLeft.back();
312 nodeMap[currNode->Owner].push_back(currNode);
313 nodeLeft.pop_back();
314 nodeLeft.insert(nodeLeft.end(), currNode->Sons.begin(), currNode->Sons.end());
317 // 2 ) build statistics
318 typedef std::vector<CTimerStat> TTimerStatVect;
319 typedef std::vector<CTimerStat *> TTimerStatPtrVect;
320 TTimerStatVect stats(nodeMap.size());
321 TTimerStatPtrVect statsPtr(stats.size());
323 uint k = 0;
324 for(TNodeMap::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it)
326 statsPtr[k] = &stats[k];
327 stats[k].Timer = it->first;
328 stats[k].buildFromNodes(&(it->second[0]), (uint)it->second.size(), _MsPerTick);
329 ++k;
332 // 3 ) sort statistics
333 if (criterion != NoSort)
335 CStatSorter sorter(criterion);
336 std::sort(statsPtr.begin(), statsPtr.end(), sorter);
339 // 4 ) get root total time.
340 CStats rootStats;
341 rootStats.buildFromNode( &_RootNode, _MsPerTick);
343 // 5 ) display statistics
344 uint maxNodeLength = 0;
345 std::string format;
346 if (displayInline)
348 for(TTimerStatPtrVect::iterator statIt = statsPtr.begin(); statIt != statsPtr.end(); ++statIt)
350 maxNodeLength = std::max(maxNodeLength, (uint)strlen((*statIt)->Timer->_Name));
352 format = "HTIMER: %-" + NLMISC::toString(maxNodeLength + 1) + "s %s";
354 std::string statsInline;
356 log->displayRawNL(format.c_str(), "", " | total | local | visits | loc%/ glb% | sessn max | min | max | mean");
358 for(TTimerStatPtrVect::iterator statIt = statsPtr.begin(); statIt != statsPtr.end(); ++statIt)
360 if (!displayInline)
362 log->displayRawNL("HTIMER: =================================");
363 log->displayRawNL("HTIMER: Node %s", (*statIt)->Timer->_Name);
364 (*statIt)->display(log, displayEx, _WantStandardDeviation);
366 else
368 (*statIt)->getStats(statsInline, displayEx, rootStats.TotalTime, _WantStandardDeviation);
369 char out[4096];
370 NLMISC::smprintf(out, 2048, format.c_str(), (*statIt)->Timer->_Name, statsInline.c_str());
371 log->displayRawNL(out);
374 benchClock.stop();
375 _CurrNode->SonsPreambule += benchClock.getNumTicks();
378 //================================================================================================
379 void CHTimer::displayByExecutionPath(CLog *log, TSortCriterion criterion, bool displayInline, bool alignPaths, bool displayEx)
381 CSimpleClock benchClock;
382 benchClock.start();
383 log->displayRawNL("HTIMER: =========================================================================");
384 log->displayRawNL("HTIMER: Bench by execution path");
385 nlassert(_BenchStartedOnce); // should have done at least one bench
387 typedef std::vector<CNodeStat> TNodeStatVect;
388 typedef std::vector<CNodeStat *> TNodeStatPtrVect;
390 TNodeStatVect nodeStats;
391 nodeStats.reserve(_RootNode.getNumNodes());
392 TNodeVect nodeLeft;
393 nodeLeft.push_back(&_RootNode);
394 /// 1 ) walk the tree to build the node map (well, in a not very optimal way..)
395 while (!nodeLeft.empty())
397 CNode *currNode = nodeLeft.back();
399 nodeStats.push_back(CNodeStat());
400 nodeStats.back().buildFromNode(currNode, _MsPerTick);
401 nodeStats.back().Node = currNode;
403 nodeLeft.pop_back();
404 nodeLeft.insert(nodeLeft.end(), currNode->Sons.begin(), currNode->Sons.end());
408 /// 2 ) sort statistics
409 // create a pointer list
410 TNodeStatPtrVect nodeStatsPtrs(nodeStats.size());
411 for(uint k = 0; k < nodeStats.size(); ++k)
413 nodeStatsPtrs[k] = &nodeStats[k];
416 // 3 ) sort statistics
417 if (criterion != NoSort)
419 CStatSorter sorter(criterion);
420 std::sort(nodeStatsPtrs.begin(), nodeStatsPtrs.end(), sorter);
423 // 4 ) get root total time.
424 CStats rootStats;
425 rootStats.buildFromNode(&_RootNode, _MsPerTick);
427 // 5 ) display statistics
428 std::string statsInline;
429 std::string nodePath;
431 std::string format;
432 if (displayInline)
434 if (alignPaths)
436 uint maxSize = 0;
437 std::string np;
438 for(TNodeStatPtrVect::iterator it = nodeStatsPtrs.begin(); it != nodeStatsPtrs.end(); ++it)
440 (*it)->Node->getPath(np);
441 maxSize = std::max(maxSize, (uint)np.size());
443 format = "HTIMER: %-" + NLMISC::toString(maxSize) +"s %s";
445 else
447 format = "HTIMER: %s %s";
451 log->displayRawNL(format.c_str(), "", " | total | local | visits | loc%/ glb% | sessn max | min | max | mean");
453 for(TNodeStatPtrVect::iterator it = nodeStatsPtrs.begin(); it != nodeStatsPtrs.end(); ++it)
455 if (!displayInline)
457 log->displayRawNL("HTIMER: =================================");
458 (*it)->Node->displayPath(log);
459 (*it)->display(log, displayEx, _WantStandardDeviation);
461 else
463 (*it)->getStats(statsInline, displayEx, rootStats.TotalTime, _WantStandardDeviation);
464 (*it)->Node->getPath(nodePath);
466 char out[2048];
467 NLMISC::smprintf(out, 2048, format.c_str(), nodePath.c_str(), statsInline.c_str());
468 log->displayRawNL(out);
471 benchClock.stop();
472 _CurrNode->SonsPreambule += benchClock.getNumTicks();
475 //=================================================================
476 /*static*/ void CHTimer::displayHierarchical(CLog *log, bool displayEx /*=true*/,uint labelNumChar /*=32*/, uint indentationStep /*= 2*/)
478 CSimpleClock benchClock;
479 benchClock.start();
480 log->displayNL("HTIMER: =========================================================================");
481 log->displayRawNL("HTIMER: Hierarchical display of bench");
482 nlassert(_BenchStartedOnce); // should have done at least one bench
483 typedef std::map<CHTimer *, TNodeVect> TNodeMap;
484 TNodeMap nodeMap;
485 TNodeVect nodeLeft;
486 nodeLeft.push_back(&_RootNode);
487 /// 1 ) walk the execution tree to build the node map (well, in a not very optimal way..)
488 while (!nodeLeft.empty())
490 CNode *currNode = nodeLeft.back();
491 nodeMap[currNode->Owner].push_back(currNode);
492 nodeLeft.pop_back();
493 nodeLeft.insert(nodeLeft.end(), currNode->Sons.begin(), currNode->Sons.end());
496 log->displayRawNL("HTIMER: %*s | total | local | visits | loc%%/ glb%% | sessn max | min | max | mean", labelNumChar, "");
498 /// 2 ) get root total time.
499 CStats rootStats;
500 rootStats.buildFromNode(&_RootNode, _MsPerTick);
502 /// 3 ) walk the timers tree and display infos (cumulate infos of nodes of each execution path)
503 CStats currNodeStats;
504 std::vector<uint> sonsIndex;
505 uint depth = 0;
506 CHTimer *currTimer = &_RootTimer;
507 sonsIndex.push_back(0);
508 bool displayStat = true;
509 std::string resultName;
510 std::string resultStats;
511 while (!sonsIndex.empty())
513 if (displayStat)
515 resultName.resize(labelNumChar);
516 std::fill(resultName.begin(), resultName.end(), '.');
517 uint startIndex = depth * indentationStep;
518 uint endIndex = std::min(startIndex + (uint)::strlen(currTimer->_Name), labelNumChar);
519 if ((sint) (endIndex - startIndex) >= 1)
521 std::copy(currTimer->_Name, currTimer->_Name + (endIndex - startIndex), resultName.begin() + startIndex);
523 TNodeVect &execNodes = nodeMap[currTimer];
524 if (!execNodes.empty())
526 currNodeStats.buildFromNodes(&execNodes[0], (uint)execNodes.size(), _MsPerTick);
527 currNodeStats.getStats(resultStats, displayEx, rootStats.TotalTime, _WantStandardDeviation);
528 log->displayRawNL("HTIMER: %s", (resultName + resultStats).c_str());
531 if (sonsIndex.back() == currTimer->_Sons.size())
533 sonsIndex.pop_back();
534 currTimer = currTimer->_Parent;
535 displayStat = false;
536 -- depth;
538 else
540 currTimer = currTimer->_Sons[sonsIndex.back()];
541 ++ sonsIndex.back();
542 sonsIndex.push_back(0);
543 displayStat = true;
544 ++ depth;
547 benchClock.stop();
548 _CurrNode->SonsPreambule += benchClock.getNumTicks();
552 //=================================================================
553 /*static*/ void CHTimer::displayHierarchicalByExecutionPath(CLog *log, bool displayEx, uint labelNumChar, uint indentationStep)
555 displayHierarchicalByExecutionPathSorted(log, NoSort, displayEx, labelNumChar, indentationStep);
559 //=================================================================
560 /*static*/ void CHTimer::displayHierarchicalByExecutionPathSorted(CLog *log, TSortCriterion criterion, bool displayEx, uint labelNumChar, uint indentationStep)
563 CSimpleClock benchClock;
564 benchClock.start();
565 nlassert(_BenchStartedOnce); // should have done at least one bench
567 // get root total time.
568 CStats rootStats;
569 rootStats.buildFromNode(&_RootNode, _MsPerTick);
572 // display header.
573 CLog::TDisplayInfo dummyDspInfo;
574 log->displayRawNL("HTIMER: =========================================================================");
575 log->displayRawNL("HTIMER: Hierarchical display of bench by execution path");
576 log->displayRawNL("HTIMER: %*s | total | local | visits | loc%%/ glb%% | sessn max | min | max | mean", labelNumChar, "");
579 // use list because vector of vector is bad.
580 std::list< CExamStackEntry > examStack;
582 // Add the root to the stack.
583 examStack.push_back( CExamStackEntry( &_RootNode ) );
584 CStats currNodeStats;
585 std::string resultName;
586 std::string resultStats;
588 while (!examStack.empty())
590 CNode *node = examStack.back().Node;
591 std::vector<CNode*> &children= examStack.back().Children;
592 uint child = examStack.back().CurrentChild;
594 // If child 0, then must first build children info and display me.
595 if (child == 0)
597 // Build Sons Infos.
598 // ==============
600 // resize array
601 children.resize(node->Sons.size());
603 // If no sort, easy.
604 if(criterion == NoSort)
606 children= node->Sons;
608 // else, Sort them with criterion.
609 else
611 std::vector<CNodeStat> stats;
612 std::vector<CNodeStat *> ptrStats;
613 stats.resize(children.size());
614 ptrStats.resize(children.size());
616 // build stats.
617 uint i;
618 for(i=0; i<children.size(); i++)
620 CNode *childNode= node->Sons[i];
621 stats[i].buildFromNode(childNode, _MsPerTick);
622 stats[i].Node = childNode;
623 ptrStats[i]= &stats[i];
626 // sort.
627 CStatSorter sorter;
628 sorter.Criterion= criterion;
629 std::sort(ptrStats.begin(), ptrStats.end(), sorter);
631 // fill children.
632 for(i=0; i<children.size(); i++)
634 children[i]= ptrStats[i]->Node;
639 // Display our infos
640 // ==============
641 // build the indented node name.
642 resultName.resize(labelNumChar);
643 std::fill(resultName.begin(), resultName.end(), '.');
644 uint startIndex = (uint)(examStack.size()-1) * indentationStep;
645 uint endIndex = std::min(startIndex + (uint)::strlen(node->Owner->_Name), labelNumChar);
646 if ((sint) (endIndex - startIndex) >= 1)
648 std::copy(node->Owner->_Name, node->Owner->_Name + (endIndex - startIndex), resultName.begin() + startIndex);
651 // build the stats string.
652 currNodeStats.buildFromNode(node, _MsPerTick);
653 currNodeStats.getStats(resultStats, displayEx, rootStats.TotalTime, _WantStandardDeviation);
655 // display
656 log->displayRawNL("HTIMER: %s", (resultName + resultStats).c_str());
659 // End of sons?? stop.
660 if (child >= children.size())
662 examStack.pop_back();
663 continue;
666 // next son.
667 ++(examStack.back().CurrentChild);
669 // process the current son.
670 examStack.push_back( CExamStackEntry( children[child] ) );
674 benchClock.stop();
675 _CurrNode->SonsPreambule += benchClock.getNumTicks();
678 //=================================================================
679 /*static*/ void CHTimer::displaySummary(CLog *log, TSortCriterion criterion, bool displayEx, uint labelNumChar, uint indentationStep, uint maxDepth)
682 CSimpleClock benchClock;
683 benchClock.start();
684 nlassert(_BenchStartedOnce); // should have done at least one bench
686 // get root total time.
687 CStats rootStats;
688 rootStats.buildFromNode(&_RootNode, _MsPerTick);
691 // display header.
692 CLog::TDisplayInfo dummyDspInfo;
693 log->displayRawNL("HTIMER: =========================================================================");
694 log->displayRawNL("HTIMER: Hierarchical display of bench by execution path");
695 log->displayRawNL("HTIMER: %*s | total | local | visits | loc%%/ glb%% | sessn max | min | max | mean", labelNumChar, "");
698 // use list because vector of vector is bad.
699 std::list< CExamStackEntry > examStack;
701 // Add the root to the stack.
702 examStack.push_back( CExamStackEntry( &_RootNode ) );
703 CStats currNodeStats;
704 std::string resultName;
705 std::string resultStats;
707 while (!examStack.empty())
709 CNode *node = examStack.back().Node;
710 std::vector<CNode*> &children= examStack.back().Children;
711 uint child = examStack.back().CurrentChild;
712 uint depth = examStack.back().Depth;
714 // If child 0, then must first build children info and display me.
715 if (child == 0)
717 // Build Sons Infos.
718 // ==============
720 // resize array
721 children.resize(node->Sons.size());
723 // If no sort, easy.
724 if(criterion == NoSort)
726 children= node->Sons;
728 // else, Sort them with criterion.
729 else
731 std::vector<CNodeStat> stats;
732 std::vector<CNodeStat *> ptrStats;
733 stats.resize(children.size());
734 ptrStats.resize(children.size());
736 // build stats.
737 uint i;
738 for(i=0; i<children.size(); i++)
740 CNode *childNode= node->Sons[i];
741 stats[i].buildFromNode(childNode, _MsPerTick);
742 stats[i].Node = childNode;
743 ptrStats[i]= &stats[i];
746 // sort.
747 CStatSorter sorter;
748 sorter.Criterion= criterion;
749 std::sort(ptrStats.begin(), ptrStats.end(), sorter);
751 // fill children.
752 for(i=0; i<children.size(); i++)
754 children[i]= ptrStats[i]->Node;
759 // Display our infos
760 // ==============
761 // build the indented node name.
762 resultName.resize(labelNumChar);
763 std::fill(resultName.begin(), resultName.end(), '.');
764 uint startIndex = (uint)(examStack.size()-1) * indentationStep;
765 uint endIndex = std::min(startIndex + (uint)::strlen(node->Owner->_Name), labelNumChar);
766 if ((sint) (endIndex - startIndex) >= 1)
768 std::copy(node->Owner->_Name, node->Owner->_Name + (endIndex - startIndex), resultName.begin() + startIndex);
771 // build the stats string.
772 currNodeStats.buildFromNode(node, _MsPerTick);
773 currNodeStats.getStats(resultStats, displayEx, rootStats.TotalTime, _WantStandardDeviation);
775 // display
776 log->displayRawNL("HTIMER: %s", (resultName + resultStats).c_str());
779 // End of sons?? stop.
780 if (child >= children.size())
782 examStack.pop_back();
783 continue;
786 // next son.
787 ++(examStack.back().CurrentChild);
789 // process the current son.
790 if (depth+1 < maxDepth)
791 examStack.push_back( CExamStackEntry( children[child], depth+1 ) );
795 benchClock.stop();
796 _CurrNode->SonsPreambule += benchClock.getNumTicks();
799 //=================================================================
800 void CHTimer::clear()
802 // should not be benching !
803 nlassert(_CurrNode == &_RootNode);
804 _RootNode.releaseSons();
805 _CurrNode = &_RootNode;
806 _RootNode.reset();
809 //=================================================================
810 void CHTimer::CStats::buildFromNode(CNode *node, double msPerTick)
812 buildFromNodes(&node, 1, msPerTick);
815 //=================================================================
816 void CHTimer::CStats::buildFromNodes(CNode **nodes, uint numNodes, double msPerTick)
818 TotalTime = 0;
819 TotalTimeWithoutSons = 0;
820 NumVisits = 0;
822 uint64 minTime = (uint64) -1;
823 uint64 maxTime = 0;
824 uint64 sessionMaxTime = 0;
826 uint k, l;
827 for(k = 0; k < numNodes; ++k)
829 TotalTime += nodes[k]->TotalTime * msPerTick;
830 TotalTimeWithoutSons += (nodes[k]->TotalTime - nodes[k]->LastSonsTotalTime) * msPerTick;
831 NumVisits += nodes[k]->NumVisits;
832 minTime = std::min(minTime, nodes[k]->MinTime);
833 maxTime = std::max(maxTime, nodes[k]->MaxTime);
834 sessionMaxTime = std::max(sessionMaxTime, nodes[k]->SessionMax);
836 if (minTime == (uint64) -1)
838 minTime = 0;
840 MinTime = minTime * msPerTick;
841 MaxTime = maxTime * msPerTick;
842 SessionMaxTime = sessionMaxTime * msPerTick;
844 if (NumVisits > 0)
845 MeanTime = TotalTime / NumVisits;
846 else
847 MeanTime = 0.0;
849 // compute standard deviation
850 double varianceSum = 0;
851 uint numMeasures = 0;
852 for(k = 0; k < numNodes; ++k)
854 numMeasures += (uint)nodes[k]->Measures.size();
855 for(l = 0; l < nodes[k]->Measures.size(); ++l)
857 varianceSum += NLMISC::sqr(nodes[k]->Measures[l] - MeanTime);
860 TimeStandardDeviation = numMeasures == 0 ? 0
861 : ::sqrt(varianceSum / (numMeasures +1));
864 //=================================================================
865 void CHTimer::CStats::display(CLog *log, bool displayEx, bool wantStandardDeviation /* = false*/)
867 log->displayRawNL("HTIMER: Total time = %.3f ms", (float) TotalTime);
868 log->displayRawNL("HTIMER: Total time without sons = %.3f ms", (float) TotalTimeWithoutSons);
869 log->displayRawNL(("HTIMER: Num visits = " + NLMISC::toString(NumVisits)).c_str());
870 if (displayEx)
872 log->displayRawNL("HTIMER: Min time = %.3f ms", (float) MinTime);
873 log->displayRawNL("HTIMER: Max time = %.3f ms", (float) MaxTime);
874 log->displayRawNL("HTIMER: Mean time = %.3f ms", (float) MeanTime);
875 if (wantStandardDeviation)
877 log->displayRawNL("HTIMER: Standard deviation = %.3f ms", (float) TimeStandardDeviation);
879 log->displayRawNL("HTIMER: Session Max time = %.3f ms", (float) SessionMaxTime);
880 //log->displayRawNL("Time standard deviation = %.3f ms", (float) TimeStandardDeviation);
885 //=================================================================
886 void CHTimer::CStats::getStats(std::string &dest, bool statEx, double rootTotalTime, bool wantStandardDeviation /*= false*/)
888 char buf[1024];
889 if (!wantStandardDeviation)
891 if (!statEx)
893 NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s ", (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str());
895 else
897 NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s | %5.1f/%5.1f | %9.3f | %9.3f | %9.3f | %9.3f",
898 (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str(),
899 float(100*TotalTimeWithoutSons/rootTotalTime), float(100*TotalTime/rootTotalTime),
900 (float) SessionMaxTime,
901 (float) MinTime, (float) MaxTime, (float) MeanTime
905 else
907 if (!statEx)
909 NLMISC::smprintf(buf, 1024, " | %10.3f | %10.3f | %12s | std deviation %9.3f", (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str(), (float) TimeStandardDeviation);
911 else
913 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",
914 (float) TotalTime, (float) TotalTimeWithoutSons, toString(NumVisits).c_str(),
915 float(100*TotalTimeWithoutSons/rootTotalTime), float(100*TotalTime/rootTotalTime),
916 (float) SessionMaxTime,
917 (float) MinTime, (float) MaxTime, (float) MeanTime,
918 (float) TimeStandardDeviation
922 dest = buf;
926 //=================================================================
927 bool CHTimer::CStatSorter::operator()(const CHTimer::CStats *lhs, const CHTimer::CStats *rhs)
929 switch(Criterion)
931 case CHTimer::TotalTime: return lhs->TotalTime >= rhs->TotalTime;
932 case CHTimer::TotalTimeWithoutSons: return lhs->TotalTimeWithoutSons >= rhs->TotalTimeWithoutSons;
933 case CHTimer::MeanTime: return lhs->MeanTime >= rhs->MeanTime;
934 case CHTimer::NumVisits: return lhs->NumVisits >= rhs->NumVisits;
935 case CHTimer::MaxTime: return lhs->MaxTime >= rhs->MaxTime;
936 case CHTimer::MinTime: return lhs->MinTime < rhs->MinTime;
937 case CHTimer::MaxSession: return lhs->SessionMaxTime > rhs->SessionMaxTime;
938 default:
939 nlassert(0); // not a valid criterion
940 break;
942 return false;
946 //===============================================
947 void CHTimer::doBefore()
949 _PreambuleClock.start();
950 walkTreeToCurrent();
951 ++ _CurrNode->NumVisits;
952 _CurrNode->SonsPreambule = 0;
953 if (!_Parent && _CurrTimer != this)
955 _Parent = _CurrTimer;
956 // register as a son of the parent
957 _Parent->_Sons.push_back(this);
959 _CurrTimer = this;
960 _PreambuleClock.stop();
961 if (_CurrNode->Parent)
963 _CurrNode->Parent->SonsPreambule += _PreambuleClock.getNumTicks();
965 _CurrNode->Clock.start();
968 //===============================================
969 void CHTimer::doAfter(bool displayAfter)
971 _CurrNode->Clock.stop();
972 _PreambuleClock.start();
973 /* Remove my Son preambule, and remove only ONE StartStop
974 It is because between the start and the end, only ONE rdtsc time is counted:
976 sint64 numTicks = _CurrNode->Clock.getNumTicks() - _CurrNode->SonsPreambule - (CSimpleClock::getStartStopNumTicks());
977 // Case where the SonPreambule is overestimated,
978 numTicks= std::max((sint64)0, numTicks);
979 // In case where the SonPreambule is overestimated, the TotalTime must not be < of the SonTime
980 if(_CurrNode->TotalTime + numTicks < _CurrNode->SonsTotalTime)
981 numTicks= _CurrNode->SonsTotalTime - _CurrNode->TotalTime;
983 _CurrNode->TotalTime += numTicks;
984 _CurrNode->MinTime = std::min(_CurrNode->MinTime, (uint64)numTicks);
985 _CurrNode->MaxTime = std::max(_CurrNode->MaxTime, (uint64)numTicks);
986 _CurrNode->LastSonsTotalTime = _CurrNode->SonsTotalTime;
988 _CurrNode->SessionCurrent += (uint64)numTicks;
990 if (displayAfter)
992 nlinfo("HTIMER: %s %.3fms loop number %d", _Name, numTicks * _MsPerTick, _CurrNode->NumVisits);
995 if (_WantStandardDeviation)
997 _CurrNode->Measures.push_back(numTicks * _MsPerTick);
1000 if (_Parent)
1002 _CurrTimer = _Parent;
1005 if (_CurrNode->Parent)
1007 CNode *curNode= _CurrNode;
1008 CNode *parent= _CurrNode->Parent;
1009 parent->SonsTotalTime += numTicks;
1010 _PreambuleClock.stop();
1012 The SonPreambule of my parent is
1013 + my BeforePreambule (counted in doBefore)
1014 + my Afterpreambule (see below)
1015 + my Sons Preambule
1016 + some constant time due to the Start/Stop of the _CurrNode->Clock, the 2* Start/Stop
1017 of the PreabmuleClock, the function call time of doBefore and doAfter
1019 parent->SonsPreambule += _PreambuleClock.getNumTicks() + curNode->SonsPreambule + _AfterStopEstimateTime;
1020 // walk to parent
1021 _CurrNode= parent;
1023 else
1025 _PreambuleClock.stop();
1033 * Clears SessionMax current stats (only current value)
1035 void CHTimer::clearSessionCurrent()
1037 _RootNode.resetSessionCurrent();
1041 * Clears all SessionMax stats (max and current values)
1043 void CHTimer::clearSessionStats()
1045 _RootNode.resetSessionStats();
1049 * Update session stats
1051 void CHTimer::updateSessionStats()
1053 if (_RootNode.SessionCurrent > _RootNode.SessionMax)
1054 _RootNode.spreadSession();
1061 // Commands
1064 #define CASE_DISPLAYMEASURES(crit, alternative) \
1065 if (args[0] == #crit || depth == alternative) \
1067 CHTimer::TSortCriterion criterion = CHTimer::crit; \
1068 if (hasDepth && depth != alternative) \
1069 CHTimer::displaySummary(&log, criterion, true, 64, 2, depth); \
1070 else \
1071 CHTimer::display(&log, criterion); \
1073 else
1075 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]")
1077 if (args.size() < 1)
1079 CHTimer::display(&log);
1080 CHTimer::displayHierarchicalByExecutionPathSorted (&log, CHTimer::TotalTime, true, 64);
1081 return true;
1084 sint depth = 0;
1085 bool hasDepth = (fromString(args[0], depth) || (args.size() > 1 && fromString(args[1], depth)));
1087 CASE_DISPLAYMEASURES(NoSort, -3)
1088 CASE_DISPLAYMEASURES(TotalTime, -2)
1089 CASE_DISPLAYMEASURES(TotalTimeWithoutSons, -4)
1090 CASE_DISPLAYMEASURES(MeanTime, -5)
1091 CASE_DISPLAYMEASURES(NumVisits, -6)
1092 CASE_DISPLAYMEASURES(MaxTime, -7)
1093 CASE_DISPLAYMEASURES(MinTime, -8)
1094 CASE_DISPLAYMEASURES(MaxSession, -9)
1096 if (hasDepth)
1097 CHTimer::displaySummary(&log, CHTimer::TotalTime, true, 64, 2, depth);
1098 else
1099 CHTimer::display(&log, CHTimer::TotalTime);
1102 return true;
1105 } // NLMISC