qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

timed-average.c (6397B)


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