Skip to content

Commit

Permalink
lib: Add generic exponentially weighted moving average (EWMA) function
Browse files Browse the repository at this point in the history
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 <br1@einfach.org>
Reviewed-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Bruno Randolf authored and linvjw committed Nov 18, 2010
1 parent 50a9432 commit c5485a7
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 0 deletions.
32 changes: 32 additions & 0 deletions include/linux/average.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef _LINUX_AVERAGE_H
#define _LINUX_AVERAGE_H

#include <linux/kernel.h>

/* Exponentially weighted moving average (EWMA) */

/* For more documentation see lib/average.c */

struct ewma {
unsigned long internal;
unsigned long factor;
unsigned long weight;
};

extern void ewma_init(struct ewma *avg, unsigned long factor,
unsigned long weight);

extern struct ewma *ewma_add(struct ewma *avg, unsigned long val);

/**
* ewma_read() - Get average value
* @avg: Average structure
*
* Returns the average value held in @avg.
*/
static inline unsigned long ewma_read(const struct ewma *avg)
{
return DIV_ROUND_CLOSEST(avg->internal, avg->factor);
}

#endif /* _LINUX_AVERAGE_H */
3 changes: 3 additions & 0 deletions lib/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,7 @@ config GENERIC_ATOMIC64
config LRU_CACHE
tristate

config AVERAGE
bool

endmenu
2 changes: 2 additions & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ obj-$(CONFIG_GENERIC_ATOMIC64) += atomic64.o

obj-$(CONFIG_ATOMIC64_SELFTEST) += atomic64_test.o

obj-$(CONFIG_AVERAGE) += average.o

hostprogs-y := gen_crc32table
clean-files := crc32table.h

Expand Down
57 changes: 57 additions & 0 deletions lib/average.c
Original file line number Diff line number Diff line change
@@ -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 <linux/module.h>
#include <linux/average.h>
#include <linux/bug.h>

/**
* 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);

0 comments on commit c5485a7

Please sign in to comment.