stop_watch.h 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Copyright 2016-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <chrono>
  18. #include <stdexcept>
  19. #include <utility>
  20. #include <folly/Chrono.h>
  21. #include <folly/portability/Time.h>
  22. namespace folly {
  23. using monotonic_clock = std::chrono::steady_clock;
  24. /**
  25. * Calculates the duration of time intervals. Prefer this over directly using
  26. * monotonic clocks. It is very lightweight and provides convenient facilitles
  27. * to avoid common pitfalls.
  28. *
  29. * There are two type aliases that should be preferred over instantiating this
  30. * class directly: `coarse_stop_watch` and `stop_watch`.
  31. *
  32. * Arguments:
  33. * - Clock: the monotonic clock to use when calculating time intervals
  34. * - Duration: (optional) the duration to use when reporting elapsed time.
  35. * Defaults to the clock's duration.
  36. *
  37. * Example 1:
  38. *
  39. * coarse_stop_watch<std::chrono::seconds> watch;
  40. * do_something();
  41. * std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
  42. *
  43. * auto const ttl = 60_s;
  44. * if (watch.elapsed(ttl)) {
  45. * process_expiration();
  46. * }
  47. *
  48. * Example 2:
  49. *
  50. * struct run_every_n_seconds {
  51. * using callback = std::function<void()>;
  52. * run_every_n_seconds(std::chrono::seconds period, callback action)
  53. * period_(period),
  54. * action_(std::move(action))
  55. * {
  56. * // watch_ is correctly initialized to the current time
  57. * }
  58. *
  59. * void run() {
  60. * while (true) {
  61. * if (watch_.lap(period_)) {
  62. * action_();
  63. * }
  64. * std::this_thread::yield();
  65. * }
  66. * }
  67. *
  68. * private:
  69. * stop_watch<> watch_;
  70. * std::chrono::seconds period_;
  71. * callback action_;
  72. * };
  73. *
  74. * @author: Marcelo Juchem <marcelo@fb.com>
  75. */
  76. template <typename Clock, typename Duration = typename Clock::duration>
  77. struct custom_stop_watch {
  78. using clock_type = Clock;
  79. using duration = Duration;
  80. using time_point = std::chrono::time_point<clock_type, duration>;
  81. static_assert(
  82. std::ratio_less_equal<
  83. typename clock_type::duration::period,
  84. typename duration::period>::value,
  85. "clock must be at least as precise as the requested duration");
  86. static_assert(
  87. Clock::is_steady,
  88. "only monotonic clocks should be used to track time intervals");
  89. /**
  90. * Initializes the stop watch with the current time as its checkpoint.
  91. *
  92. * Example:
  93. *
  94. * stop_watch<> watch;
  95. * do_something();
  96. * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
  97. *
  98. * @author: Marcelo Juchem <marcelo@fb.com>
  99. */
  100. custom_stop_watch() : checkpoint_(clock_type::now()) {}
  101. /**
  102. * Initializes the stop watch with the given time as its checkpoint.
  103. *
  104. * NOTE: this constructor should be seldomly used. It is only provided so
  105. * that, in the rare occasions it is needed, one does not have to reimplement
  106. * the `custom_stop_watch` class.
  107. *
  108. * Example:
  109. *
  110. * custom_stop_watch<monotonic_clock> watch(monotonic_clock::now());
  111. * do_something();
  112. * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
  113. *
  114. * @author: Marcelo Juchem <marcelo@fb.com>
  115. */
  116. explicit custom_stop_watch(typename clock_type::time_point checkpoint)
  117. : checkpoint_(std::move(checkpoint)) {}
  118. /**
  119. * Updates the stop watch checkpoint to the current time.
  120. *
  121. * Example:
  122. *
  123. * struct some_resource {
  124. * // ...
  125. *
  126. * void on_reloaded() {
  127. * time_alive.reset();
  128. * }
  129. *
  130. * void report() {
  131. * std::cout << "resource has been alive for " << time_alive.elapsed();
  132. * }
  133. *
  134. * private:
  135. * stop_watch<> time_alive;
  136. * };
  137. *
  138. * @author: Marcelo Juchem <marcelo@fb.com>
  139. */
  140. void reset() {
  141. checkpoint_ = clock_type::now();
  142. }
  143. /**
  144. * Tells the elapsed time since the last update.
  145. *
  146. * The stop watch's checkpoint remains unchanged.
  147. *
  148. * Example:
  149. *
  150. * stop_watch<> watch;
  151. * do_something();
  152. * std::cout << "time elapsed: " << watch.elapsed() << std::endl;
  153. *
  154. * @author: Marcelo Juchem <marcelo@fb.com>
  155. */
  156. duration elapsed() const {
  157. return std::chrono::duration_cast<duration>(
  158. clock_type::now() - checkpoint_);
  159. }
  160. /**
  161. * Tells whether the given duration has already elapsed since the last
  162. * checkpoint.
  163. *
  164. * Example:
  165. *
  166. * auto const ttl = 60_s;
  167. * stop_watch<> watch;
  168. *
  169. * do_something();
  170. *
  171. * std::cout << "has the TTL expired? " std::boolalpha<< watch.elapsed(ttl);
  172. *
  173. * @author: Marcelo Juchem <marcelo@fb.com>
  174. */
  175. template <typename UDuration>
  176. bool elapsed(UDuration&& amount) const {
  177. return clock_type::now() - checkpoint_ >= amount;
  178. }
  179. /**
  180. * Tells the elapsed time since the last update, and updates the checkpoint
  181. * to the current time.
  182. *
  183. * Example:
  184. *
  185. * struct some_resource {
  186. * // ...
  187. *
  188. * void on_reloaded() {
  189. * auto const alive = time_alive.lap();
  190. * std::cout << "resource reloaded after being alive for " << alive;
  191. * }
  192. *
  193. * private:
  194. * stop_watch<> time_alive;
  195. * };
  196. *
  197. * @author: Marcelo Juchem <marcelo@fb.com>
  198. */
  199. duration lap() {
  200. auto lastCheckpoint = checkpoint_;
  201. checkpoint_ = clock_type::now();
  202. return std::chrono::duration_cast<duration>(checkpoint_ - lastCheckpoint);
  203. }
  204. /**
  205. * Tells whether the given duration has already elapsed since the last
  206. * checkpoint. If so, update the checkpoint to the current time. If not,
  207. * the checkpoint remains unchanged.
  208. *
  209. * Example:
  210. *
  211. * void run_every_n_seconds(
  212. * std::chrono::seconds period,
  213. * std::function<void()> action
  214. * ) {
  215. * for (stop_watch<> watch;; ) {
  216. * if (watch.lap(period)) {
  217. * action();
  218. * }
  219. * std::this_thread::yield();
  220. * }
  221. * }
  222. *
  223. * @author: Marcelo Juchem <marcelo@fb.com>
  224. */
  225. template <typename UDuration>
  226. bool lap(UDuration&& amount) {
  227. auto now = clock_type::now();
  228. if (now - checkpoint_ < amount) {
  229. return false;
  230. }
  231. checkpoint_ = now;
  232. return true;
  233. }
  234. /**
  235. * Returns the current checkpoint
  236. */
  237. typename clock_type::time_point getCheckpoint() const {
  238. return checkpoint_;
  239. }
  240. private:
  241. typename clock_type::time_point checkpoint_;
  242. };
  243. /**
  244. * A type alias for `custom_stop_watch` that uses a coarse monotonic clock as
  245. * the time source. Refer to the documentation of `custom_stop_watch` for full
  246. * documentation.
  247. *
  248. * Arguments:
  249. * - Duration: (optional) the duration to use when reporting elapsed time.
  250. * Defaults to the clock's duration.
  251. *
  252. * Example:
  253. *
  254. * coarse_stop_watch<std::chrono::seconds> watch;
  255. * do_something();
  256. * std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
  257. *
  258. * @author: Marcelo Juchem <marcelo@fb.com>
  259. */
  260. template <typename Duration = folly::chrono::coarse_steady_clock::duration>
  261. using coarse_stop_watch =
  262. custom_stop_watch<folly::chrono::coarse_steady_clock, Duration>;
  263. /**
  264. * A type alias for `custom_stop_watch` that uses a monotonic clock as the time
  265. * source. Refer to the documentation of `custom_stop_watch` for full
  266. * documentation.
  267. *
  268. * Arguments:
  269. * - Duration: (optional) the duration to use when reporting elapsed time.
  270. * Defaults to the clock's duration.
  271. *
  272. * Example:
  273. *
  274. * stop_watch<std::chrono::seconds> watch;
  275. * do_something();
  276. * std::cout << "time elapsed: " << watch.elapsed().count() << std::endl;
  277. *
  278. * @author: Marcelo Juchem <marcelo@fb.com>
  279. */
  280. template <typename Duration = std::chrono::steady_clock::duration>
  281. using stop_watch = custom_stop_watch<std::chrono::steady_clock, Duration>;
  282. } // namespace folly