Profiling.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include <Engine/Base/Profiling.h>
  14. #include <Engine/Templates/StaticArray.cpp>
  15. template CStaticArray<CProfileCounter>;
  16. template CStaticArray<CProfileTimer>;
  17. static inline __int64 ReadTSC_profile(void)
  18. {
  19. __int64 mmRet;
  20. __asm {
  21. rdtsc
  22. mov dword ptr [mmRet+0],eax
  23. mov dword ptr [mmRet+4],edx
  24. }
  25. return mmRet;
  26. }
  27. /////////////////////////////////////////////////////////////////////
  28. // CProfileForm
  29. // how much it takes to start a profiling timer
  30. CTimerValue _tvStartEpsilon;
  31. // how much it takes to stop a profiling timer
  32. CTimerValue _tvStopEpsilon;
  33. // how much it takes to just start and stop a profiling timer (measure zero time)
  34. CTimerValue _tvStartStopEpsilon;
  35. // total sum of all timing offsets induced by all Starts and Stops so far
  36. CTimerValue _tvCurrentProfilingEpsilon;
  37. // Measure profiling errors and set epsilon corrections.
  38. static CTimerValue _tvTest;
  39. void CProfileForm::CalibrateProfilingTimers(void)
  40. {
  41. enum Epsilons {
  42. ETI_TOTAL,
  43. ETI_STARTSTOP,
  44. ETI_START,
  45. ETI_STOP,
  46. ETI_DUMMY,
  47. ETI_COUNT
  48. };
  49. CProfileForm pfCalibration("", "", 0, ETI_COUNT);
  50. // set all epsilons to zero, so that they don't interfere with the measuring
  51. _tvStartEpsilon.Clear();
  52. _tvStopEpsilon.Clear();
  53. _tvStartStopEpsilon.Clear();
  54. _tvCurrentProfilingEpsilon.Clear();
  55. /* NOTE: we must use one more counter (ETI_TOTAL), so that we don't overestimate
  56. * time spent in StartTimer(), StopTimer() if it is the first timer started.
  57. */
  58. #define REPEATCOUNT 10000
  59. // measure how much it takes to start and stop timer
  60. __int64 llMinStartStopTime(0x7fffffffffffffff);
  61. {for (INDEX i=0; i<REPEATCOUNT; i++) {
  62. pfCalibration.Reset();
  63. pfCalibration.StartTimer(ETI_TOTAL);
  64. pfCalibration.StartTimer(ETI_STARTSTOP);
  65. pfCalibration.StopTimer(ETI_STARTSTOP);
  66. pfCalibration.StopTimer(ETI_TOTAL);
  67. __int64 llThisStartStopTime = pfCalibration.pf_aptTimers[ETI_STARTSTOP].
  68. pt_tvElapsed.tv_llValue;
  69. if (llThisStartStopTime<llMinStartStopTime) {
  70. llMinStartStopTime = llThisStartStopTime;
  71. }
  72. }}
  73. _tvStartStopEpsilon = llMinStartStopTime;
  74. // measure how much it takes to start timer
  75. __int64 llMinStartTime(0x7fffffffffffffff);
  76. {for (INDEX i=0; i<REPEATCOUNT; i++) {
  77. pfCalibration.Reset();
  78. pfCalibration.StartTimer(ETI_TOTAL);
  79. pfCalibration.StartTimer(ETI_START);
  80. pfCalibration.StartTimer(ETI_DUMMY);
  81. pfCalibration.StopTimer(ETI_START);
  82. pfCalibration.StopTimer(ETI_TOTAL);
  83. pfCalibration.StopTimer(ETI_DUMMY);
  84. __int64 llThisStartTime = pfCalibration.pf_aptTimers[ETI_START].
  85. pt_tvElapsed.tv_llValue;
  86. if (llThisStartTime<llMinStartTime) {
  87. llMinStartTime = llThisStartTime;
  88. }
  89. }}
  90. _tvStartEpsilon = llMinStartTime;
  91. // measure how much it takes to stop timer
  92. __int64 llMinStopTime(0x7fffffffffffffff);
  93. {for (INDEX i=0; i<REPEATCOUNT; i++) {
  94. pfCalibration.Reset();
  95. pfCalibration.StartTimer(ETI_TOTAL);
  96. pfCalibration.StartTimer(ETI_DUMMY);
  97. pfCalibration.StartTimer(ETI_STOP);
  98. pfCalibration.StopTimer(ETI_DUMMY);
  99. pfCalibration.StopTimer(ETI_STOP);
  100. pfCalibration.StopTimer(ETI_TOTAL);
  101. __int64 llThisStopTime = pfCalibration.pf_aptTimers[ETI_STOP].
  102. pt_tvElapsed.tv_llValue;
  103. if (llThisStopTime<llMinStopTime) {
  104. llMinStopTime = llThisStopTime;
  105. }
  106. }}
  107. _tvStopEpsilon = llMinStopTime;
  108. pfCalibration.Reset();
  109. pfCalibration.StartTimer(ETI_TOTAL);
  110. pfCalibration.StartTimer(ETI_STARTSTOP);
  111. pfCalibration.StopTimer(ETI_STARTSTOP);
  112. pfCalibration.StopTimer(ETI_TOTAL);
  113. _tvTest = pfCalibration.pf_aptTimers[ETI_STARTSTOP].pt_tvElapsed;
  114. }
  115. /*
  116. * Constructor for profile form with given number of counters and timers.
  117. */
  118. CProfileForm::CProfileForm(
  119. const CTString &strTitle, const CTString &strAveragingUnits,
  120. INDEX ctCounters, INDEX ctTimers)
  121. {
  122. // remember title and averaging units
  123. pf_strTitle = strTitle;
  124. pf_strAveragingUnits = strAveragingUnits;
  125. // create given number of counters
  126. pf_apcCounters.New(ctCounters);
  127. // create given number of timers
  128. pf_aptTimers.New(ctTimers);
  129. // reset all values
  130. pf_ctRunningTimers = 0;
  131. // for all timers
  132. FOREACHINSTATICARRAY(pf_aptTimers, CProfileTimer, itpt) {
  133. // clear the timer
  134. itpt->pt_tvElapsed.Clear();
  135. itpt->pt_tvStarted.tv_llValue = -__int64(1);
  136. itpt->pt_ctAveraging = 0;
  137. }
  138. }
  139. void CProfileForm::Clear(void)
  140. {
  141. pf_strTitle.Clear();
  142. pf_strAveragingUnits.Clear();
  143. pf_apcCounters.Clear();
  144. pf_aptTimers.Clear();
  145. }
  146. /*
  147. * Get name of a counter.
  148. */
  149. const CTString &CProfileForm::GetCounterName(INDEX iCounter)
  150. {
  151. return pf_apcCounters[iCounter].pc_strName;
  152. }
  153. /*
  154. * Get name of a timer.
  155. */
  156. const CTString &CProfileForm::GetTimerName(INDEX iTimer)
  157. {
  158. return pf_aptTimers[iTimer].pt_strName;
  159. }
  160. /* Start a timer. */
  161. void CProfileForm::StartTimer_internal(INDEX iTimer)
  162. {
  163. CProfileTimer &pt = pf_aptTimers[iTimer];
  164. //ASSERT(pt.pt_tvStarted.tv_llValue<0);
  165. CTimerValue tvNow = CTimerValue(ReadTSC_profile())-_tvCurrentProfilingEpsilon;
  166. pt.pt_tvStarted = tvNow;
  167. pf_ctRunningTimers++;
  168. if (pf_ctRunningTimers==1) {
  169. pf_tvOverAllStarted = tvNow;
  170. }
  171. _tvCurrentProfilingEpsilon += _tvStartEpsilon;
  172. }
  173. /* Stop a timer. */
  174. void CProfileForm::StopTimer_internal(INDEX iTimer)
  175. {
  176. CProfileTimer &pt = pf_aptTimers[iTimer];
  177. //ASSERT(pt.pt_tvStarted.tv_llValue>0);
  178. CTimerValue tvNow = CTimerValue(ReadTSC_profile())-_tvCurrentProfilingEpsilon;
  179. pt.pt_tvElapsed +=
  180. tvNow - pf_aptTimers[iTimer].pt_tvStarted - _tvStartStopEpsilon + _tvStartEpsilon;
  181. pf_ctRunningTimers--;
  182. if (pf_ctRunningTimers==0) {
  183. pf_tvOverAllElapsed += tvNow-pf_tvOverAllStarted;
  184. }
  185. IFDEBUG(pt.pt_tvStarted.tv_llValue = -__int64(1));
  186. _tvCurrentProfilingEpsilon += _tvStopEpsilon;
  187. }
  188. /* Get current value of a counter. */
  189. INDEX CProfileForm::GetCounterCount(INDEX iCounter) {
  190. return pf_apcCounters[iCounter].pc_ctCount;
  191. };
  192. // override to provide external averaging
  193. INDEX CProfileForm::GetAveragingCounter(void)
  194. {
  195. return pf_ctAveragingCounter;
  196. }
  197. /* Get current value of a timer in seconds or in percentage of module time. */
  198. double CProfileForm::GetTimerAverageTime(INDEX iTimer) {
  199. // must not report while some timers are active!
  200. ASSERT(pf_ctRunningTimers==0);
  201. return pf_aptTimers[iTimer].pt_tvElapsed.GetSeconds()/GetAveragingCounter();
  202. }
  203. double CProfileForm::GetTimerPercentageOfModule(INDEX iTimer) {
  204. // must not report while some timers are active!
  205. ASSERT(pf_ctRunningTimers==0);
  206. return pf_aptTimers[iTimer].pt_tvElapsed.GetSeconds()
  207. /pf_tvOverAllElapsed.GetSeconds()*100;
  208. }
  209. /* Get percentage of module time in application time. */
  210. double CProfileForm::GetModulePercentage(void) {
  211. // must not report while some timers are active!
  212. ASSERT(pf_ctRunningTimers==0);
  213. // calculate total application time since profile form was reset
  214. CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
  215. CTimerValue tvApplicationElapsed = tvNow-pf_tvLastReset;
  216. return pf_tvOverAllElapsed.GetSeconds()/tvApplicationElapsed.GetSeconds()*100;
  217. }
  218. /*
  219. * Reset all profiling values.
  220. */
  221. void CProfileForm::Reset(void)
  222. {
  223. // must not reset profiling form while some timers are active!
  224. //ASSERT(pf_ctRunningTimers==0);
  225. // remember time when form was reset
  226. pf_tvLastReset = _pTimer->GetHighPrecisionTimer();
  227. // clear global entries
  228. pf_tvOverAllElapsed.Clear();
  229. pf_ctAveragingCounter = 0;
  230. // for all counters
  231. FOREACHINSTATICARRAY(pf_apcCounters, CProfileCounter, itpc) {
  232. // clear the counter
  233. itpc->pc_ctCount = 0;
  234. }
  235. // for all timers
  236. FOREACHINSTATICARRAY(pf_aptTimers, CProfileTimer, itpt) {
  237. // clear the timer
  238. itpt->pt_tvElapsed.Clear();
  239. itpt->pt_tvStarted.tv_llValue = -__int64(1);
  240. itpt->pt_ctAveraging = 0;
  241. }
  242. }
  243. /* Print one counter in report. */
  244. void CProfileCounter::Report(char *&strBuffer, INDEX ctAveragingCount)
  245. {
  246. if (ctAveragingCount==0) {
  247. ctAveragingCount = 1;
  248. }
  249. strBuffer += sprintf(strBuffer, "%-45s: %7d %7.2f\n",
  250. pc_strName, pc_ctCount, (double)pc_ctCount/ctAveragingCount);
  251. }
  252. /* Print one timer in report. */
  253. void CProfileTimer::Report(char *&strBuffer,
  254. INDEX ctAveragingCount,
  255. CTimerValue tvAppElapsed, CTimerValue tvModElapsed)
  256. {
  257. if (ctAveragingCount==0) {
  258. ctAveragingCount = 1;
  259. }
  260. if (pt_strAveragingName=="") {
  261. strBuffer += sprintf(strBuffer, "%-45s: %6.2f%% %6.2f%% %6.2f ms\n",
  262. pt_strName,
  263. pt_tvElapsed.GetSeconds()/tvAppElapsed.GetSeconds()*100,
  264. pt_tvElapsed.GetSeconds()/tvModElapsed.GetSeconds()*100,
  265. pt_tvElapsed.GetSeconds()/ctAveragingCount*1000
  266. );
  267. } else {
  268. INDEX ctLocalAveraging = pt_ctAveraging;
  269. if (ctLocalAveraging==0) {
  270. ctLocalAveraging = 1;
  271. }
  272. strBuffer += sprintf(strBuffer, "%-45s: %6.2f%% %6.2f%% %6.2f ms (%4.0fc/%s x%d)\n",
  273. pt_strName,
  274. pt_tvElapsed.GetSeconds()/tvAppElapsed.GetSeconds()*100,
  275. pt_tvElapsed.GetSeconds()/tvModElapsed.GetSeconds()*100,
  276. pt_tvElapsed.GetSeconds()/ctAveragingCount*1000,
  277. pt_tvElapsed.GetSeconds()/ctLocalAveraging*_pTimer->tm_llCPUSpeedHZ,
  278. pt_strAveragingName,
  279. pt_ctAveraging/ctAveragingCount
  280. );
  281. }
  282. }
  283. /*
  284. * Report profiling results.
  285. */
  286. void CProfileForm::Report(CTString &strReport)
  287. {
  288. static char aBuffer[16000];
  289. char *strBuffer = aBuffer;
  290. // must not report profiling form while some timers are active!
  291. if (pf_ctRunningTimers!=0) {
  292. strBuffer += sprintf(strBuffer,
  293. "WARNING: Some timers are still active - the results are wrong!\n");
  294. }
  295. //ASSERT(pf_ctRunningTimers==0);
  296. // calculate total application time since profile form was reset
  297. CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
  298. CTimerValue tvApplicationElapsed = tvNow-pf_tvLastReset;
  299. // calculate total time spent in profiled module
  300. CTimerValue tvModuleElapsed = pf_tvOverAllElapsed;
  301. // print the main header
  302. strBuffer += sprintf(strBuffer, "%s profile for last %d %s:\n",
  303. pf_strTitle, GetAveragingCounter(), pf_strAveragingUnits);
  304. // print header for timers
  305. strBuffer += sprintf(strBuffer,
  306. "Module time: %6.2f%% of application time. Average time: %6.2fms\n",
  307. tvModuleElapsed.GetSeconds()/tvApplicationElapsed.GetSeconds()*100.0,
  308. tvModuleElapsed.GetSeconds()/GetAveragingCounter()*1000.0
  309. );
  310. strBuffer += sprintf(strBuffer, "\n");
  311. // for all timers
  312. FOREACHINSTATICARRAY(pf_aptTimers, CProfileTimer, itpt) {
  313. // print the timer
  314. itpt->Report(strBuffer, GetAveragingCounter(), tvApplicationElapsed, tvModuleElapsed);
  315. }
  316. strBuffer += sprintf(strBuffer, "\n");
  317. // for all counters
  318. FOREACHINSTATICARRAY(pf_apcCounters, CProfileCounter, itpc) {
  319. // print the counter
  320. itpc->Report(strBuffer, GetAveragingCounter());
  321. }
  322. // print the footer
  323. strBuffer += sprintf(strBuffer, "--------------------\n");
  324. // return the buffer
  325. strReport = aBuffer;
  326. }