Commit af556884 authored by Bruno Randolf's avatar Bruno Randolf Committed by John W. Linville

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: default avatarBruno Randolf <br1@einfach.org>

--

v2:	use log2.h functions
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5dcc03fe
#ifndef _LINUX_AVERAGE_H #ifndef _LINUX_AVERAGE_H
#define _LINUX_AVERAGE_H #define _LINUX_AVERAGE_H
#include <linux/kernel.h>
/* Exponentially weighted moving average (EWMA) */ /* Exponentially weighted moving average (EWMA) */
/* For more documentation see lib/average.c */ /* For more documentation see lib/average.c */
...@@ -26,7 +24,7 @@ extern struct ewma *ewma_add(struct ewma *avg, unsigned long val); ...@@ -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) 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 */ #endif /* _LINUX_AVERAGE_H */
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/average.h> #include <linux/average.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/log2.h>
/** /**
* DOC: Exponentially Weighted Moving Average (EWMA) * DOC: Exponentially Weighted Moving Average (EWMA)
...@@ -24,18 +25,21 @@ ...@@ -24,18 +25,21 @@
* ewma_init() - Initialize EWMA parameters * ewma_init() - Initialize EWMA parameters
* @avg: Average structure * @avg: Average structure
* @factor: Factor to use for the scaled up internal value. The maximum value * @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 * @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. * Initialize the EWMA parameters for a given struct ewma @avg.
*/ */
void ewma_init(struct ewma *avg, unsigned long factor, unsigned long weight) 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->internal = 0;
avg->weight = weight;
avg->factor = factor;
} }
EXPORT_SYMBOL(ewma_init); EXPORT_SYMBOL(ewma_init);
...@@ -49,9 +53,9 @@ EXPORT_SYMBOL(ewma_init); ...@@ -49,9 +53,9 @@ EXPORT_SYMBOL(ewma_init);
struct ewma *ewma_add(struct ewma *avg, unsigned long val) struct ewma *ewma_add(struct ewma *avg, unsigned long val)
{ {
avg->internal = avg->internal ? avg->internal = avg->internal ?
(((avg->internal * (avg->weight - 1)) + (((avg->internal << avg->weight) - avg->internal) +
(val * avg->factor)) / avg->weight) : (val << avg->factor)) >> avg->weight :
(val * avg->factor); (val << avg->factor);
return avg; return avg;
} }
EXPORT_SYMBOL(ewma_add); EXPORT_SYMBOL(ewma_add);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment