2 * QEMU timed average computation
4 * Copyright (C) Nodalink, EURL. 2014
5 * Copyright (C) Igalia, S.L. 2015
8 * BenoƮt Canet <benoit.canet@nodalink.com>
9 * Alberto Garcia <berto@igalia.com>
11 * This program is free sofware: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Sofware Foundation, either version 2 of the License, or
14 * (at your option) version 3 or any later version.
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.
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/>.
27 #include "qemu/timed-average.h"
29 /* This module computes an average of a set of values within a time
34 * - Create two windows with a certain expiration period, and
35 * offsetted by period / 2.
36 * - Each time you want to account a new value, do it in both windows.
37 * - The minimum / maximum / average values are always returned from
42 * t=0 |t=0.5 |t=1 |t=1.5 |t=2
43 * wnd0: [0,0.5)|wnd0: [0.5,1.5) | |wnd0: [1.5,2.5) |
44 * wnd1: [0,1) | |wnd1: [1,2) | |
46 * Values are returned from:
48 * wnd0---------|wnd1------------|wnd0---------|wnd1-------------|
51 /* Update the expiration of a time window
54 * @now: the current time in nanoseconds
55 * @period: the expiration period in nanoseconds
57 static void update_expiration(TimedAverageWindow
*w
, int64_t now
,
60 /* time elapsed since the last theoretical expiration */
61 int64_t elapsed
= (now
- w
->expiration
) % period
;
62 /* time remaininging until the next expiration */
63 int64_t remaining
= period
- elapsed
;
64 /* compute expiration */
65 w
->expiration
= now
+ remaining
;
70 * @w: the window to reset
72 static void window_reset(TimedAverageWindow
*w
)
80 /* Get the current window (that is, the one with the earliest
83 * @ta: the TimedAverage structure
84 * @ret: a pointer to the current window
86 static TimedAverageWindow
*current_window(TimedAverage
*ta
)
88 return &ta
->windows
[ta
->current
];
91 /* Initialize a TimedAverage structure
93 * @ta: the TimedAverage structure
94 * @clock_type: the type of clock to use
95 * @period: the time window period in nanoseconds
97 void timed_average_init(TimedAverage
*ta
, QEMUClockType clock_type
,
100 int64_t now
= qemu_clock_get_ns(clock_type
);
102 /* Returned values are from the oldest window, so they belong to
103 * the interval [ta->period/2,ta->period). By adjusting the
104 * requested period by 4/3, we guarantee that they're in the
105 * interval [2/3 period,4/3 period), closer to the requested
106 * period on average */
107 ta
->period
= (uint64_t) period
* 4 / 3;
108 ta
->clock_type
= clock_type
;
111 window_reset(&ta
->windows
[0]);
112 window_reset(&ta
->windows
[1]);
114 /* Both windows are offsetted by half a period */
115 ta
->windows
[0].expiration
= now
+ ta
->period
/ 2;
116 ta
->windows
[1].expiration
= now
+ ta
->period
;
119 /* Check if the time windows have expired, updating their counters and
120 * expiration time if that's the case.
122 * @ta: the TimedAverage structure
124 static void check_expirations(TimedAverage
*ta
)
126 int64_t now
= qemu_clock_get_ns(ta
->clock_type
);
129 assert(ta
->period
!= 0);
131 /* Check if the windows have expired */
132 for (i
= 0; i
< 2; i
++) {
133 TimedAverageWindow
*w
= &ta
->windows
[i
];
134 if (w
->expiration
<= now
) {
136 update_expiration(w
, now
, ta
->period
);
140 /* Make ta->current point to the oldest window */
141 if (ta
->windows
[0].expiration
< ta
->windows
[1].expiration
) {
150 * @ta: the TimedAverage structure
151 * @value: the value to account
153 void timed_average_account(TimedAverage
*ta
, uint64_t value
)
156 check_expirations(ta
);
158 /* Do the accounting in both windows at the same time */
159 for (i
= 0; i
< 2; i
++) {
160 TimedAverageWindow
*w
= &ta
->windows
[i
];
165 if (value
< w
->min
) {
169 if (value
> w
->max
) {
175 /* Get the minimum value
177 * @ta: the TimedAverage structure
178 * @ret: the minimum value
180 uint64_t timed_average_min(TimedAverage
*ta
)
182 TimedAverageWindow
*w
;
183 check_expirations(ta
);
184 w
= current_window(ta
);
185 return w
->min
< UINT64_MAX
? w
->min
: 0;
188 /* Get the average value
190 * @ta: the TimedAverage structure
191 * @ret: the average value
193 uint64_t timed_average_avg(TimedAverage
*ta
)
195 TimedAverageWindow
*w
;
196 check_expirations(ta
);
197 w
= current_window(ta
);
198 return w
->count
> 0 ? w
->sum
/ w
->count
: 0;
201 /* Get the maximum value
203 * @ta: the TimedAverage structure
204 * @ret: the maximum value
206 uint64_t timed_average_max(TimedAverage
*ta
)
208 check_expirations(ta
);
209 return current_window(ta
)->max
;