Skip to content

Commit

Permalink
lib: Improve EWMA efficiency by using bitshifts
Browse files Browse the repository at this point in the history
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 <[email protected]>

--

v2:	use log2.h functions
Signed-off-by: John W. Linville <[email protected]>
  • Loading branch information
Bruno Randolf authored and linvjw committed Dec 6, 2010
1 parent 5dcc03f commit af55688
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 11 deletions.
4 changes: 1 addition & 3 deletions include/linux/average.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#ifndef _LINUX_AVERAGE_H
#define _LINUX_AVERAGE_H

#include <linux/kernel.h>

/* Exponentially weighted moving average (EWMA) */

/* For more documentation see lib/average.c */
Expand All @@ -26,7 +24,7 @@ extern struct ewma *ewma_add(struct ewma *avg, unsigned long val);
*/
static inline unsigned long ewma_read(const struct ewma *avg)
{
return DIV_ROUND_CLOSEST(avg->internal, avg->factor);
return avg->internal >> avg->factor;
}

#endif /* _LINUX_AVERAGE_H */
20 changes: 12 additions & 8 deletions lib/average.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/average.h>
#include <linux/bug.h>
#include <linux/log2.h>

/**
* DOC: Exponentially Weighted Moving Average (EWMA)
Expand All @@ -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);

Expand All @@ -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);

0 comments on commit af55688

Please sign in to comment.