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