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