timed-average.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * QEMU timed average computation
  3. *
  4. * Copyright (C) Nodalink, EURL. 2014
  5. * Copyright (C) Igalia, S.L. 2015
  6. *
  7. * Authors:
  8. * Benoît Canet <benoit.canet@nodalink.com>
  9. * Alberto Garcia <berto@igalia.com>
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation, either version 2 of the License, or
  14. * (at your option) version 3 or any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. */
  24. #include "qemu/osdep.h"
  25. #include "qemu/timed-average.h"
  26. /* This module computes an average of a set of values within a time
  27. * window.
  28. *
  29. * Algorithm:
  30. *
  31. * - Create two windows with a certain expiration period, and
  32. * offsetted by period / 2.
  33. * - Each time you want to account a new value, do it in both windows.
  34. * - The minimum / maximum / average values are always returned from
  35. * the oldest window.
  36. *
  37. * Example:
  38. *
  39. * t=0 |t=0.5 |t=1 |t=1.5 |t=2
  40. * wnd0: [0,0.5)|wnd0: [0.5,1.5) | |wnd0: [1.5,2.5) |
  41. * wnd1: [0,1) | |wnd1: [1,2) | |
  42. *
  43. * Values are returned from:
  44. *
  45. * wnd0---------|wnd1------------|wnd0---------|wnd1-------------|
  46. */
  47. /* Update the expiration of a time window
  48. *
  49. * @w: the window used
  50. * @now: the current time in nanoseconds
  51. * @period: the expiration period in nanoseconds
  52. */
  53. static void update_expiration(TimedAverageWindow *w, int64_t now,
  54. int64_t period)
  55. {
  56. /* time elapsed since the last theoretical expiration */
  57. int64_t elapsed = (now - w->expiration) % period;
  58. /* time remaininging until the next expiration */
  59. int64_t remaining = period - elapsed;
  60. /* compute expiration */
  61. w->expiration = now + remaining;
  62. }
  63. /* Reset a window
  64. *
  65. * @w: the window to reset
  66. */
  67. static void window_reset(TimedAverageWindow *w)
  68. {
  69. w->min = UINT64_MAX;
  70. w->max = 0;
  71. w->sum = 0;
  72. w->count = 0;
  73. }
  74. /* Get the current window (that is, the one with the earliest
  75. * expiration time).
  76. *
  77. * @ta: the TimedAverage structure
  78. * @ret: a pointer to the current window
  79. */
  80. static TimedAverageWindow *current_window(TimedAverage *ta)
  81. {
  82. return &ta->windows[ta->current];
  83. }
  84. /* Initialize a TimedAverage structure
  85. *
  86. * @ta: the TimedAverage structure
  87. * @clock_type: the type of clock to use
  88. * @period: the time window period in nanoseconds
  89. */
  90. void timed_average_init(TimedAverage *ta, QEMUClockType clock_type,
  91. uint64_t period)
  92. {
  93. int64_t now = qemu_clock_get_ns(clock_type);
  94. /* Returned values are from the oldest window, so they belong to
  95. * the interval [ta->period/2,ta->period). By adjusting the
  96. * requested period by 4/3, we guarantee that they're in the
  97. * interval [2/3 period,4/3 period), closer to the requested
  98. * period on average */
  99. ta->period = (uint64_t) period * 4 / 3;
  100. ta->clock_type = clock_type;
  101. ta->current = 0;
  102. window_reset(&ta->windows[0]);
  103. window_reset(&ta->windows[1]);
  104. /* Both windows are offsetted by half a period */
  105. ta->windows[0].expiration = now + ta->period / 2;
  106. ta->windows[1].expiration = now + ta->period;
  107. }
  108. /* Check if the time windows have expired, updating their counters and
  109. * expiration time if that's the case.
  110. *
  111. * @ta: the TimedAverage structure
  112. * @elapsed: if non-NULL, the elapsed time (in ns) within the current
  113. * window will be stored here
  114. */
  115. static void check_expirations(TimedAverage *ta, uint64_t *elapsed)
  116. {
  117. int64_t now = qemu_clock_get_ns(ta->clock_type);
  118. int i;
  119. assert(ta->period != 0);
  120. /* Check if the windows have expired */
  121. for (i = 0; i < 2; i++) {
  122. TimedAverageWindow *w = &ta->windows[i];
  123. if (w->expiration <= now) {
  124. window_reset(w);
  125. update_expiration(w, now, ta->period);
  126. }
  127. }
  128. /* Make ta->current point to the oldest window */
  129. if (ta->windows[0].expiration < ta->windows[1].expiration) {
  130. ta->current = 0;
  131. } else {
  132. ta->current = 1;
  133. }
  134. /* Calculate the elapsed time within the current window */
  135. if (elapsed) {
  136. int64_t remaining = ta->windows[ta->current].expiration - now;
  137. *elapsed = ta->period - remaining;
  138. }
  139. }
  140. /* Account a value
  141. *
  142. * @ta: the TimedAverage structure
  143. * @value: the value to account
  144. */
  145. void timed_average_account(TimedAverage *ta, uint64_t value)
  146. {
  147. int i;
  148. check_expirations(ta, NULL);
  149. /* Do the accounting in both windows at the same time */
  150. for (i = 0; i < 2; i++) {
  151. TimedAverageWindow *w = &ta->windows[i];
  152. w->sum += value;
  153. w->count++;
  154. if (value < w->min) {
  155. w->min = value;
  156. }
  157. if (value > w->max) {
  158. w->max = value;
  159. }
  160. }
  161. }
  162. /* Get the minimum value
  163. *
  164. * @ta: the TimedAverage structure
  165. * @ret: the minimum value
  166. */
  167. uint64_t timed_average_min(TimedAverage *ta)
  168. {
  169. TimedAverageWindow *w;
  170. check_expirations(ta, NULL);
  171. w = current_window(ta);
  172. return w->min < UINT64_MAX ? w->min : 0;
  173. }
  174. /* Get the average value
  175. *
  176. * @ta: the TimedAverage structure
  177. * @ret: the average value
  178. */
  179. uint64_t timed_average_avg(TimedAverage *ta)
  180. {
  181. TimedAverageWindow *w;
  182. check_expirations(ta, NULL);
  183. w = current_window(ta);
  184. return w->count > 0 ? w->sum / w->count : 0;
  185. }
  186. /* Get the maximum value
  187. *
  188. * @ta: the TimedAverage structure
  189. * @ret: the maximum value
  190. */
  191. uint64_t timed_average_max(TimedAverage *ta)
  192. {
  193. check_expirations(ta, NULL);
  194. return current_window(ta)->max;
  195. }
  196. /* Get the sum of all accounted values
  197. * @ta: the TimedAverage structure
  198. * @elapsed: if non-NULL, the elapsed time (in ns) will be stored here
  199. * @ret: the sum of all accounted values
  200. */
  201. uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed)
  202. {
  203. TimedAverageWindow *w;
  204. check_expirations(ta, elapsed);
  205. w = current_window(ta);
  206. return w->sum;
  207. }