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