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