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