 cb8d4c8f54
			
		
	
	
		cb8d4c8f54
		
	
	
	
	
		
			
			Signed-off-by: Stefan Weil <sw@weilnetz.de> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
		
			
				
	
	
		
			232 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU timed average computation
 | |
|  *
 | |
|  * Copyright (C) Nodalink, EURL. 2014
 | |
|  * Copyright (C) Igalia, S.L. 2015
 | |
|  *
 | |
|  * Authors:
 | |
|  *   Benoît Canet <benoit.canet@nodalink.com>
 | |
|  *   Alberto Garcia <berto@igalia.com>
 | |
|  *
 | |
|  * This program is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation, either version 2 of the License, or
 | |
|  * (at your option) version 3 or any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| 
 | |
| #include "qemu/timed-average.h"
 | |
| 
 | |
| /* This module computes an average of a set of values within a time
 | |
|  * window.
 | |
|  *
 | |
|  * Algorithm:
 | |
|  *
 | |
|  * - Create two windows with a certain expiration period, and
 | |
|  *   offsetted by period / 2.
 | |
|  * - Each time you want to account a new value, do it in both windows.
 | |
|  * - The minimum / maximum / average values are always returned from
 | |
|  *   the oldest window.
 | |
|  *
 | |
|  * Example:
 | |
|  *
 | |
|  *        t=0          |t=0.5           |t=1          |t=1.5            |t=2
 | |
|  *        wnd0: [0,0.5)|wnd0: [0.5,1.5) |             |wnd0: [1.5,2.5)  |
 | |
|  *        wnd1: [0,1)  |                |wnd1: [1,2)  |                 |
 | |
|  *
 | |
|  * Values are returned from:
 | |
|  *
 | |
|  *        wnd0---------|wnd1------------|wnd0---------|wnd1-------------|
 | |
|  */
 | |
| 
 | |
| /* Update the expiration of a time window
 | |
|  *
 | |
|  * @w:      the window used
 | |
|  * @now:    the current time in nanoseconds
 | |
|  * @period: the expiration period in nanoseconds
 | |
|  */
 | |
| static void update_expiration(TimedAverageWindow *w, int64_t now,
 | |
|                               int64_t period)
 | |
| {
 | |
|     /* time elapsed since the last theoretical expiration */
 | |
|     int64_t elapsed = (now - w->expiration) % period;
 | |
|     /* time remaininging until the next expiration */
 | |
|     int64_t remaining = period - elapsed;
 | |
|     /* compute expiration */
 | |
|     w->expiration = now + remaining;
 | |
| }
 | |
| 
 | |
| /* Reset a window
 | |
|  *
 | |
|  * @w: the window to reset
 | |
|  */
 | |
| static void window_reset(TimedAverageWindow *w)
 | |
| {
 | |
|     w->min = UINT64_MAX;
 | |
|     w->max = 0;
 | |
|     w->sum = 0;
 | |
|     w->count = 0;
 | |
| }
 | |
| 
 | |
| /* Get the current window (that is, the one with the earliest
 | |
|  * expiration time).
 | |
|  *
 | |
|  * @ta:  the TimedAverage structure
 | |
|  * @ret: a pointer to the current window
 | |
|  */
 | |
| static TimedAverageWindow *current_window(TimedAverage *ta)
 | |
| {
 | |
|      return &ta->windows[ta->current];
 | |
| }
 | |
| 
 | |
| /* Initialize a TimedAverage structure
 | |
|  *
 | |
|  * @ta:         the TimedAverage structure
 | |
|  * @clock_type: the type of clock to use
 | |
|  * @period:     the time window period in nanoseconds
 | |
|  */
 | |
| void timed_average_init(TimedAverage *ta, QEMUClockType clock_type,
 | |
|                         uint64_t period)
 | |
| {
 | |
|     int64_t now = qemu_clock_get_ns(clock_type);
 | |
| 
 | |
|     /* Returned values are from the oldest window, so they belong to
 | |
|      * the interval [ta->period/2,ta->period). By adjusting the
 | |
|      * requested period by 4/3, we guarantee that they're in the
 | |
|      * interval [2/3 period,4/3 period), closer to the requested
 | |
|      * period on average */
 | |
|     ta->period = (uint64_t) period * 4 / 3;
 | |
|     ta->clock_type = clock_type;
 | |
|     ta->current = 0;
 | |
| 
 | |
|     window_reset(&ta->windows[0]);
 | |
|     window_reset(&ta->windows[1]);
 | |
| 
 | |
|     /* Both windows are offsetted by half a period */
 | |
|     ta->windows[0].expiration = now + ta->period / 2;
 | |
|     ta->windows[1].expiration = now + ta->period;
 | |
| }
 | |
| 
 | |
| /* Check if the time windows have expired, updating their counters and
 | |
|  * expiration time if that's the case.
 | |
|  *
 | |
|  * @ta: the TimedAverage structure
 | |
|  * @elapsed: if non-NULL, the elapsed time (in ns) within the current
 | |
|  *           window will be stored here
 | |
|  */
 | |
| static void check_expirations(TimedAverage *ta, uint64_t *elapsed)
 | |
| {
 | |
|     int64_t now = qemu_clock_get_ns(ta->clock_type);
 | |
|     int i;
 | |
| 
 | |
|     assert(ta->period != 0);
 | |
| 
 | |
|     /* Check if the windows have expired */
 | |
|     for (i = 0; i < 2; i++) {
 | |
|         TimedAverageWindow *w = &ta->windows[i];
 | |
|         if (w->expiration <= now) {
 | |
|             window_reset(w);
 | |
|             update_expiration(w, now, ta->period);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Make ta->current point to the oldest window */
 | |
|     if (ta->windows[0].expiration < ta->windows[1].expiration) {
 | |
|         ta->current = 0;
 | |
|     } else {
 | |
|         ta->current = 1;
 | |
|     }
 | |
| 
 | |
|     /* Calculate the elapsed time within the current window */
 | |
|     if (elapsed) {
 | |
|         int64_t remaining = ta->windows[ta->current].expiration - now;
 | |
|         *elapsed = ta->period - remaining;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Account a value
 | |
|  *
 | |
|  * @ta:    the TimedAverage structure
 | |
|  * @value: the value to account
 | |
|  */
 | |
| void timed_average_account(TimedAverage *ta, uint64_t value)
 | |
| {
 | |
|     int i;
 | |
|     check_expirations(ta, NULL);
 | |
| 
 | |
|     /* Do the accounting in both windows at the same time */
 | |
|     for (i = 0; i < 2; i++) {
 | |
|         TimedAverageWindow *w = &ta->windows[i];
 | |
| 
 | |
|         w->sum += value;
 | |
|         w->count++;
 | |
| 
 | |
|         if (value < w->min) {
 | |
|             w->min = value;
 | |
|         }
 | |
| 
 | |
|         if (value > w->max) {
 | |
|             w->max = value;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Get the minimum value
 | |
|  *
 | |
|  * @ta:  the TimedAverage structure
 | |
|  * @ret: the minimum value
 | |
|  */
 | |
| uint64_t timed_average_min(TimedAverage *ta)
 | |
| {
 | |
|     TimedAverageWindow *w;
 | |
|     check_expirations(ta, NULL);
 | |
|     w = current_window(ta);
 | |
|     return w->min < UINT64_MAX ? w->min : 0;
 | |
| }
 | |
| 
 | |
| /* Get the average value
 | |
|  *
 | |
|  * @ta:  the TimedAverage structure
 | |
|  * @ret: the average value
 | |
|  */
 | |
| uint64_t timed_average_avg(TimedAverage *ta)
 | |
| {
 | |
|     TimedAverageWindow *w;
 | |
|     check_expirations(ta, NULL);
 | |
|     w = current_window(ta);
 | |
|     return w->count > 0 ? w->sum / w->count : 0;
 | |
| }
 | |
| 
 | |
| /* Get the maximum value
 | |
|  *
 | |
|  * @ta:  the TimedAverage structure
 | |
|  * @ret: the maximum value
 | |
|  */
 | |
| uint64_t timed_average_max(TimedAverage *ta)
 | |
| {
 | |
|     check_expirations(ta, NULL);
 | |
|     return current_window(ta)->max;
 | |
| }
 | |
| 
 | |
| /* Get the sum of all accounted values
 | |
|  * @ta:      the TimedAverage structure
 | |
|  * @elapsed: if non-NULL, the elapsed time (in ns) will be stored here
 | |
|  * @ret:     the sum of all accounted values
 | |
|  */
 | |
| uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed)
 | |
| {
 | |
|     TimedAverageWindow *w;
 | |
|     check_expirations(ta, elapsed);
 | |
|     w = current_window(ta);
 | |
|     return w->sum;
 | |
| }
 |