1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
26 #include "nel/misc/time_nl.h"
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();
74 measuringClock
.stop();
76 _StartStopNumTicks
= (measuringClock
.getNumTicks() >> 1) / numSamples
;
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());
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;
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()
114 for(uint k
= 0; k
< Sons
.size(); ++k
)
118 //=================================================================
119 void CHTimer::CNode::releaseSons()
121 for(uint k
= 0; k
< Sons
.size(); ++k
)
126 //=================================================================
127 void CHTimer::CNode::displayPath(CLog
*log
) const
131 log
->displayRawNL(("HTIMER: " + path
).c_str());
134 //=================================================================
135 void CHTimer::CNode::getPath(std::string
&path
) const
138 const CNode
*currNode
= this;
141 path
= path
.empty() ? currNode
->Owner
->getName()
142 : currNode
->Owner
->getName() + std::string("::") + path
;
143 currNode
= currNode
->Parent
;
149 //=================================================================
150 uint
CHTimer::CNode::getNumNodes() const
153 for(uint k
= 0; k
< Sons
.size(); ++k
)
155 sum
+= Sons
[k
]->getNumNodes();
161 //=================================================================
162 void CHTimer::walkTreeToCurrent()
166 for(uint k
= 0; k
< _CurrNode
->Sons
.size(); ++k
)
168 if (_CurrNode
->Sons
[k
]->Owner
== this)
170 _CurrNode
= _CurrNode
->Sons
[k
];
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
)
191 const uint numSamples
= 1000;
193 // Do as in startBench, reset and init
198 double freq
= (double) CSystemInfo::getProcessorFrequency(false);
199 _MsPerTick
= 1000 / (double) freq
;
201 _MsPerTick
= CTime::ticksToSecond(1000);
203 CSimpleClock::init();
208 _BenchStartedOnce
= true;
209 _RootNode
.Owner
= &_RootTimer
;
210 _WantStandardDeviation
= false;
213 for(uint i
=0;i
<numSamples
;i
++)
215 static NLMISC::CHTimer
estimateSampleTimer("sampleTimer");
216 estimateSampleTimer
.before();
217 estimateSampleTimer
.after();
223 // Then the After Stop time is the rootTimer time / numSamples
224 _AfterStopEstimateTime
= (_RootNode
.TotalTime
-_RootNode
.SonsTotalTime
) / numSamples
;
226 _AfterStopEstimateTimeDone
= true;
233 //=================================================================
234 void CHTimer::startBench(bool wantStandardDeviation
/*= false*/, bool quick
, bool reset
)
236 nlassert(!_Benching
);
238 // if not done, estimate the AfterStopTime
239 estimateAfterStopTime();
247 double freq
= (double) CSystemInfo::getProcessorFrequency(quick
);
248 _MsPerTick
= 1000 / (double) freq
;
250 _MsPerTick
= CTime::ticksToSecond(1000);
252 CSimpleClock::init();
255 // if reset needed, clearup all
259 // clear current for session
260 clearSessionCurrent();
264 _BenchStartedOnce
= true;
265 _RootNode
.Owner
= &_RootTimer
;
266 _WantStandardDeviation
= wantStandardDeviation
;
270 //=================================================================
271 void CHTimer::endBench()
276 if (_CurrNode
== &_RootNode
)
282 nlwarning("HTIMER: Stopping the bench inside a benched functions !");
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
;
295 if(!_BenchStartedOnce
) // should have done at least one bench
298 _CurrNode
->SonsPreambule
+= benchClock
.getNumTicks();
301 log
->displayNL("HTIMER: =========================================================================");
302 log
->displayRawNL("HTIMER: Bench cumuled results");
303 typedef std::map
<CHTimer
*, TNodeVect
> TNodeMap
;
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
);
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());
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
);
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.
341 rootStats
.buildFromNode( &_RootNode
, _MsPerTick
);
343 // 5 ) display statistics
344 uint maxNodeLength
= 0;
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
)
362 log
->displayRawNL("HTIMER: =================================");
363 log
->displayRawNL("HTIMER: Node %s", (*statIt
)->Timer
->_Name
);
364 (*statIt
)->display(log
, displayEx
, _WantStandardDeviation
);
368 (*statIt
)->getStats(statsInline
, displayEx
, rootStats
.TotalTime
, _WantStandardDeviation
);
370 NLMISC::smprintf(out
, 2048, format
.c_str(), (*statIt
)->Timer
->_Name
, statsInline
.c_str());
371 log
->displayRawNL(out
);
375 _CurrNode
->SonsPreambule
+= benchClock
.getNumTicks();
378 //================================================================================================
379 void CHTimer::displayByExecutionPath(CLog
*log
, TSortCriterion criterion
, bool displayInline
, bool alignPaths
, bool displayEx
)
381 CSimpleClock benchClock
;
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());
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
;
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.
425 rootStats
.buildFromNode(&_RootNode
, _MsPerTick
);
427 // 5 ) display statistics
428 std::string statsInline
;
429 std::string nodePath
;
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";
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
)
457 log
->displayRawNL("HTIMER: =================================");
458 (*it
)->Node
->displayPath(log
);
459 (*it
)->display(log
, displayEx
, _WantStandardDeviation
);
463 (*it
)->getStats(statsInline
, displayEx
, rootStats
.TotalTime
, _WantStandardDeviation
);
464 (*it
)->Node
->getPath(nodePath
);
467 NLMISC::smprintf(out
, 2048, format
.c_str(), nodePath
.c_str(), statsInline
.c_str());
468 log
->displayRawNL(out
);
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
;
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
;
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
);
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.
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
;
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())
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
;
540 currTimer
= currTimer
->_Sons
[sonsIndex
.back()];
542 sonsIndex
.push_back(0);
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
;
565 nlassert(_BenchStartedOnce
); // should have done at least one bench
567 // get root total time.
569 rootStats
.buildFromNode(&_RootNode
, _MsPerTick
);
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.
601 children
.resize(node
->Sons
.size());
604 if(criterion
== NoSort
)
606 children
= node
->Sons
;
608 // else, Sort them with criterion.
611 std::vector
<CNodeStat
> stats
;
612 std::vector
<CNodeStat
*> ptrStats
;
613 stats
.resize(children
.size());
614 ptrStats
.resize(children
.size());
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
];
628 sorter
.Criterion
= criterion
;
629 std::sort(ptrStats
.begin(), ptrStats
.end(), sorter
);
632 for(i
=0; i
<children
.size(); i
++)
634 children
[i
]= ptrStats
[i
]->Node
;
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
);
656 log
->displayRawNL("HTIMER: %s", (resultName
+ resultStats
).c_str());
659 // End of sons?? stop.
660 if (child
>= children
.size())
662 examStack
.pop_back();
667 ++(examStack
.back().CurrentChild
);
669 // process the current son.
670 examStack
.push_back( CExamStackEntry( children
[child
] ) );
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
;
684 nlassert(_BenchStartedOnce
); // should have done at least one bench
686 // get root total time.
688 rootStats
.buildFromNode(&_RootNode
, _MsPerTick
);
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.
721 children
.resize(node
->Sons
.size());
724 if(criterion
== NoSort
)
726 children
= node
->Sons
;
728 // else, Sort them with criterion.
731 std::vector
<CNodeStat
> stats
;
732 std::vector
<CNodeStat
*> ptrStats
;
733 stats
.resize(children
.size());
734 ptrStats
.resize(children
.size());
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
];
748 sorter
.Criterion
= criterion
;
749 std::sort(ptrStats
.begin(), ptrStats
.end(), sorter
);
752 for(i
=0; i
<children
.size(); i
++)
754 children
[i
]= ptrStats
[i
]->Node
;
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
);
776 log
->displayRawNL("HTIMER: %s", (resultName
+ resultStats
).c_str());
779 // End of sons?? stop.
780 if (child
>= children
.size())
782 examStack
.pop_back();
787 ++(examStack
.back().CurrentChild
);
789 // process the current son.
790 if (depth
+1 < maxDepth
)
791 examStack
.push_back( CExamStackEntry( children
[child
], depth
+1 ) );
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
;
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
)
819 TotalTimeWithoutSons
= 0;
822 uint64 minTime
= (uint64
) -1;
824 uint64 sessionMaxTime
= 0;
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)
840 MinTime
= minTime
* msPerTick
;
841 MaxTime
= maxTime
* msPerTick
;
842 SessionMaxTime
= sessionMaxTime
* msPerTick
;
845 MeanTime
= TotalTime
/ NumVisits
;
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());
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*/)
889 if (!wantStandardDeviation
)
893 NLMISC::smprintf(buf
, 1024, " | %10.3f | %10.3f | %12s ", (float) TotalTime
, (float) TotalTimeWithoutSons
, toString(NumVisits
).c_str());
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
909 NLMISC::smprintf(buf
, 1024, " | %10.3f | %10.3f | %12s | std deviation %9.3f", (float) TotalTime
, (float) TotalTimeWithoutSons
, toString(NumVisits
).c_str(), (float) TimeStandardDeviation
);
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
926 //=================================================================
927 bool CHTimer::CStatSorter::operator()(const CHTimer::CStats
*lhs
, const CHTimer::CStats
*rhs
)
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
;
939 nlassert(0); // not a valid criterion
946 //===============================================
947 void CHTimer::doBefore()
949 _PreambuleClock
.start();
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);
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
;
992 nlinfo("HTIMER: %s %.3fms loop number %d", _Name
, numTicks
* _MsPerTick
, _CurrNode
->NumVisits
);
995 if (_WantStandardDeviation
)
997 _CurrNode
->Measures
.push_back(numTicks
* _MsPerTick
);
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)
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
;
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();
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); \
1071 CHTimer::display(&log, criterion); \
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);
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)
1097 CHTimer::displaySummary(&log
, CHTimer::TotalTime
, true, 64, 2, depth
);
1099 CHTimer::display(&log
, CHTimer::TotalTime
);