From c5485a7e7569ab32eea240c850198519e2a765ef Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Tue, 16 Nov 2010 10:58:37 +0900 Subject: lib: Add generic exponentially weighted moving average (EWMA) function This adds generic functions for calculating Exponentially Weighted Moving Averages (EWMA). This implementation makes use of a structure which keeps the EWMA parameters and a scaled up internal representation to reduce rounding errors. The original idea for this implementation came from the rt2x00 driver (rt2x00link.c). I would like to use it in several places in the mac80211 and ath5k code and I hope it can be useful in many other places in the kernel code. Signed-off-by: Bruno Randolf Reviewed-by: KOSAKI Motohiro Signed-off-by: John W. Linville --- lib/average.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 lib/average.c (limited to 'lib/average.c') diff --git a/lib/average.c b/lib/average.c new file mode 100644 index 000000000000..f1d1b4660c42 --- /dev/null +++ b/lib/average.c @@ -0,0 +1,57 @@ +/* + * lib/average.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include + +/** + * DOC: Exponentially Weighted Moving Average (EWMA) + * + * These are generic functions for calculating Exponentially Weighted Moving + * Averages (EWMA). We keep a structure with the EWMA parameters and a scaled + * up internal representation of the average value to prevent rounding errors. + * The factor for scaling up and the exponential weight (or decay rate) have to + * be specified thru the init fuction. The structure should not be accessed + * directly but only thru the helper functions. + */ + +/** + * ewma_init() - Initialize EWMA parameters + * @avg: Average structure + * @factor: Factor to use for the scaled up internal value. The maximum value + * of averages can be ULONG_MAX/(factor*weight). + * @weight: Exponential weight, or decay rate. This defines how fast the + * influence of older values decreases. Has to be bigger than 1. + * + * Initialize the EWMA parameters for a given struct ewma @avg. + */ +void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight) +{ + WARN_ON(weight <= 1 || factor == 0); + avg->internal = 0; + avg->weight = weight; + avg->factor = factor; +} +EXPORT_SYMBOL(ewma_init); + +/** + * ewma_add() - Exponentially weighted moving average (EWMA) + * @avg: Average structure + * @val: Current value + * + * Add a sample to the average. + */ +struct ewma *ewma_add(struct ewma *avg, unsigned long val) +{ + avg->internal = avg->internal ? + (((avg->internal * (avg->weight - 1)) + + (val * avg->factor)) / avg->weight) : + (val * avg->factor); + return avg; +} +EXPORT_SYMBOL(ewma_add); -- cgit v1.2.2 From af5568843594fb71055debe36e521fa8072fcecc Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Thu, 2 Dec 2010 19:50:37 +0900 Subject: lib: Improve EWMA efficiency by using bitshifts Using bitshifts instead of division and multiplication should improve performance. That requires weight and factor to be powers of two, but i think this is something we can live with. Thanks to Peter Zijlstra for the improved formula! Signed-off-by: Bruno Randolf -- v2: use log2.h functions Signed-off-by: John W. Linville --- lib/average.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'lib/average.c') diff --git a/lib/average.c b/lib/average.c index f1d1b4660c42..5576c2841496 100644 --- a/lib/average.c +++ b/lib/average.c @@ -8,6 +8,7 @@ #include #include #include +#include /** * DOC: Exponentially Weighted Moving Average (EWMA) @@ -24,18 +25,21 @@ * ewma_init() - Initialize EWMA parameters * @avg: Average structure * @factor: Factor to use for the scaled up internal value. The maximum value - * of averages can be ULONG_MAX/(factor*weight). + * of averages can be ULONG_MAX/(factor*weight). For performance reasons + * factor has to be a power of 2. * @weight: Exponential weight, or decay rate. This defines how fast the - * influence of older values decreases. Has to be bigger than 1. + * influence of older values decreases. For performance reasons weight has + * to be a power of 2. * * Initialize the EWMA parameters for a given struct ewma @avg. */ void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight) { - WARN_ON(weight <= 1 || factor == 0); + WARN_ON(!is_power_of_2(weight) || !is_power_of_2(factor)); + + avg->weight = ilog2(weight); + avg->factor = ilog2(factor); avg->internal = 0; - avg->weight = weight; - avg->factor = factor; } EXPORT_SYMBOL(ewma_init); @@ -49,9 +53,9 @@ EXPORT_SYMBOL(ewma_init); struct ewma *ewma_add(struct ewma *avg, unsigned long val) { avg->internal = avg->internal ? - (((avg->internal * (avg->weight - 1)) + - (val * avg->factor)) / avg->weight) : - (val * avg->factor); + (((avg->internal << avg->weight) - avg->internal) + + (val << avg->factor)) >> avg->weight : + (val << avg->factor); return avg; } EXPORT_SYMBOL(ewma_add); -- cgit v1.2.2