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