/worker/cppworker/utility/Timer.cpp

https://github.com/paypal/hera · C++ · 310 lines · 209 code · 44 blank · 57 comment · 8 complexity · 22b03f5f70226f21e35224be00a2c620 MD5 · raw file

  1. // Copyright 2020 PayPal Inc.
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to You under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. #include <sstream>
  18. #include <unistd.h>
  19. #include "utility/Assert.h"
  20. #include "Timer.h"
  21. namespace
  22. {
  23. unsigned long long convert_ticks_to_seconds(unsigned long long ticks, uint divisor)
  24. {
  25. // Right here, it shows the problem with using '_' before identifier. It is reserved!
  26. static long clk_tck = ::sysconf(_SC_CLK_TCK);
  27. ASSERT( clk_tck > 0 );
  28. // Converts to microseconds before returning in precision.
  29. return ((ticks * 1000000) / clk_tck / divisor);
  30. }
  31. double convert_ticks_to_seconds(double ticks, uint divisor)
  32. {
  33. // Right here, it shows the problem with using '_' before identifier. It is reserved!
  34. static long clk_tck = ::sysconf(_SC_CLK_TCK);
  35. ASSERT( clk_tck > 0 );
  36. // Converts to microseconds before returning in precision.
  37. return ((ticks * 1000000.0) / clk_tck / divisor);
  38. }
  39. };
  40. Timer::Timer() :
  41. m_id(0),
  42. m_sample_count(0),
  43. m_default_precision(MILLISECOND)
  44. {
  45. set_id();
  46. clear();
  47. }
  48. Timer::Timer(Precision default_precision) :
  49. m_id(0),
  50. m_sample_count(0),
  51. m_default_precision(default_precision)
  52. {
  53. set_id();
  54. clear();
  55. }
  56. /**
  57. * @brief Reset the timer to initial state which means
  58. * m_marker = m_now = current time (i.e. an implicit start() is called)
  59. * m_total = m_delta = 0 (clear the cumulative total time elapsed, and the last delta)
  60. * and m_sample_count = 0 (clear the sample counter)
  61. */
  62. void Timer::clear()
  63. {
  64. set_now();
  65. m_sample_count = 0;
  66. m_wallclock.clear();
  67. m_sysclock.clear();
  68. m_userclock.clear();
  69. }
  70. /**
  71. * @brief Starts the timer and sets the marker to now.
  72. */
  73. void Timer::start()
  74. {
  75. set_now();
  76. m_wallclock.start();
  77. m_sysclock.start();
  78. m_userclock.start();
  79. }
  80. /**
  81. * @brief Stop the timer, and report the time elapsed from marker
  82. * @return The time elapsed since the marker.
  83. */
  84. unsigned long long Timer::stop()
  85. {
  86. set_now();
  87. m_wallclock.stop();
  88. m_sysclock.stop();
  89. m_userclock.stop();
  90. ++m_sample_count;
  91. return m_wallclock.m_delta;
  92. }
  93. /**
  94. * @brief Just like doing a stop() and then a start() immediately.
  95. * @return the time elapsed since the marker is set.
  96. */
  97. unsigned long long Timer::mark()
  98. {
  99. set_now();
  100. m_wallclock.mark();
  101. m_sysclock.mark();
  102. m_userclock.mark();
  103. ++m_sample_count;
  104. return m_wallclock.m_delta;
  105. }
  106. /**
  107. * @brief Get the unit for the precision. Default is microsecond.
  108. * @return A string for the time unit.
  109. */
  110. const char *Timer::get_unit(Precision p) const
  111. {
  112. switch (p)
  113. {
  114. case MILLISECOND:
  115. return "ms";
  116. case SECOND:
  117. return "s";
  118. case MINUTE:
  119. return "min";
  120. case MICROSECOND:
  121. default:
  122. break;
  123. }
  124. return "us";
  125. }
  126. /**
  127. * @brief Get the divisor for returning in the precision unit.
  128. * @return An integer that we use to divide the deltas by. Default to 1.
  129. */
  130. uint Timer::get_precision_divisor(Precision p) const
  131. {
  132. switch (p)
  133. {
  134. case MILLISECOND:
  135. return 1000;
  136. case SECOND:
  137. return 1000000;
  138. case MINUTE:
  139. return 60000000;
  140. case MICROSECOND:
  141. default:
  142. break;
  143. }
  144. return 1;
  145. }
  146. /**
  147. * @brief Return the name of the timer
  148. */
  149. std::string Timer::get_name() const
  150. {
  151. std::ostringstream os;
  152. os << "Timer[" << m_id << "]: ";
  153. return os.str();
  154. }
  155. unsigned long long Timer::get_system_delta_in_precision(Precision p) const
  156. {
  157. return convert_ticks_to_seconds(m_sysclock.m_delta, get_precision_divisor(p));
  158. }
  159. unsigned long long Timer::get_user_delta_in_precision(Precision p) const
  160. {
  161. return convert_ticks_to_seconds(m_userclock.m_delta, get_precision_divisor(p));
  162. }
  163. unsigned long long Timer::get_system_total_in_precision(Precision p) const
  164. {
  165. return convert_ticks_to_seconds(m_sysclock.m_total, get_precision_divisor(p));
  166. }
  167. unsigned long long Timer::get_user_total_in_precision(Precision p) const
  168. {
  169. return convert_ticks_to_seconds(m_userclock.m_total, get_precision_divisor(p));
  170. }
  171. double Timer::get_system_average_in_precision(Precision p) const
  172. {
  173. return convert_ticks_to_seconds(get_system_average(), get_precision_divisor(p));
  174. }
  175. double Timer::get_user_average_in_precision(Precision p) const
  176. {
  177. return convert_ticks_to_seconds(get_user_average(), get_precision_divisor(p));
  178. }
  179. std::string Timer::get_string(Precision p) const
  180. {
  181. std::string out;
  182. format_string(out, p);
  183. return out;
  184. }
  185. std::string Timer::get_string() const
  186. {
  187. return get_string(m_default_precision);
  188. }
  189. std::string Timer::get_detailed_string(Precision p) const
  190. {
  191. std::string out;
  192. format_detailed_string(out, p);
  193. return out;
  194. }
  195. std::string Timer::get_detailed_string() const
  196. {
  197. return get_detailed_string(m_default_precision);
  198. }
  199. /**
  200. * @brief Generate the output string. To customize, override this method.
  201. * @param out Reference to the string that we'll append the timer message to.
  202. * @param p Precision we want the output to be in
  203. */
  204. void Timer::format_string(std::string &out, Precision p) const
  205. {
  206. std::string unit(get_unit(p));
  207. unsigned long long delta=get_delta_in_precision(p);
  208. unsigned long long total=get_total_in_precision(p);
  209. std::ostringstream os;
  210. os << get_name() << delta << unit;
  211. if (m_wallclock.m_delta != m_wallclock.m_total)
  212. {
  213. os << ",total=" << total << unit;
  214. }
  215. out += os.str();
  216. }
  217. /**
  218. * @brief Generate the detailed output string, including system + user clock numbers.
  219. * @param out Reference to the output buffer.
  220. */
  221. void Timer::format_detailed_string(std::string &out, Precision p) const
  222. {
  223. std::string unit(get_unit(p));
  224. unsigned long long delta=get_delta_in_precision(p);
  225. unsigned long long total=get_total_in_precision(p);
  226. out.append(get_name());
  227. std::ostringstream os;
  228. os << delta << unit << ",sys=" << get_system_delta_in_precision(p) << ",user=" << get_user_delta_in_precision(p);
  229. if (m_wallclock.m_delta != m_wallclock.m_total)
  230. {
  231. os << "; total=" << total << unit << ",sys=" << get_system_total_in_precision(p) << ",user=" << get_user_total_in_precision(p);
  232. }
  233. out.append(os.str());
  234. }
  235. void Timer::set_marker(const struct timeval &tv)
  236. {
  237. m_wallclock.m_marker = (unsigned long long)tv.tv_sec * 1000000ULL + (unsigned long long)tv.tv_usec;
  238. }
  239. void Timer::set_now(const struct timeval &tv)
  240. {
  241. m_wallclock.m_now = (unsigned long long)tv.tv_sec * 1000000ULL + (unsigned long long)tv.tv_usec;
  242. }
  243. void Timer::set_id()
  244. {
  245. static unsigned int s_id = 0;
  246. m_id = ++s_id;
  247. }
  248. void Timer::set_marker(const struct tms &ticks)
  249. {
  250. m_sysclock.m_marker = ticks.tms_stime;
  251. m_userclock.m_marker = ticks.tms_utime;
  252. }
  253. void Timer::set_now(const struct tms &ticks)
  254. {
  255. m_sysclock.m_now = ticks.tms_stime;
  256. m_userclock.m_now = ticks.tms_utime;
  257. }
  258. void Timer::set_now()
  259. {
  260. struct timeval tv;
  261. gettimeofday(&tv, NULL);
  262. set_now(tv);
  263. struct tms ticks;
  264. if (::times(&ticks) != -1)
  265. set_now(ticks);
  266. }