* [PATCH] Add generic exponentially weighted moving average function @ 2010-10-06 9:32 Bruno Randolf 2010-10-13 0:33 ` Randy Dunlap 2010-10-13 14:01 ` kevin granade 0 siblings, 2 replies; 10+ messages in thread From: Bruno Randolf @ 2010-10-06 9:32 UTC (permalink / raw) To: linux-kernel This adds a generic exponentially weighted moving average function. This implementation makes use of a structure which keeps a scaled up internal representation to reduce rounding errors. The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) and i would like to use it in several places in the mac80211 and ath5k code. Signed-off-by: Bruno Randolf <br1@einfach.org> -- Is this the right place to add it? Who to CC:? --- include/linux/average.h | 32 ++++++++++++++++++++++++++++++++ 1 files changed, 32 insertions(+), 0 deletions(-) create mode 100644 include/linux/average.h diff --git a/include/linux/average.h b/include/linux/average.h new file mode 100644 index 0000000..2a00d3d --- /dev/null +++ b/include/linux/average.h @@ -0,0 +1,32 @@ +#ifndef _LINUX_AVERAGE_H +#define _LINUX_AVERAGE_H + +#define AVG_FACTOR 1000 + +struct avg_val { + int value; + int internal; +}; + +/** + * moving_average - Exponentially weighted moving average + * @avg: average structure + * @val: current value + * @samples: number of samples + * + * This implementation make use of a struct avg_val to prevent rounding + * errors. + */ +static inline struct avg_val +moving_average(const struct avg_val avg, const int val, const int samples) +{ + struct avg_val ret; + ret.internal = avg.internal ? + (((avg.internal * (samples - 1)) + + (val * AVG_FACTOR)) / samples) : + (val * AVG_FACTOR); + ret.value = ret.internal / AVG_FACTOR; + return ret; +} + +#endif /* _LINUX_AVERAGE_H */ ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-06 9:32 [PATCH] Add generic exponentially weighted moving average function Bruno Randolf @ 2010-10-13 0:33 ` Randy Dunlap 2010-10-13 2:10 ` Bruno Randolf 2010-10-15 3:40 ` Ben Pfaff 2010-10-13 14:01 ` kevin granade 1 sibling, 2 replies; 10+ messages in thread From: Randy Dunlap @ 2010-10-13 0:33 UTC (permalink / raw) To: Bruno Randolf; +Cc: linux-kernel, akpm [-- Attachment #1: Type: text/plain, Size: 2872 bytes --] On Wed, 06 Oct 2010 18:32:25 +0900 Bruno Randolf wrote: > This adds a generic exponentially weighted moving average function. This > implementation makes use of a structure which keeps a scaled up internal > representation to reduce rounding errors. > > The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) > and i would like to use it in several places in the mac80211 and ath5k code. > > Signed-off-by: Bruno Randolf <br1@einfach.org> I guess I don't understand "exponentially weighted" or why that would be desirable. Please try to explain (briefly). I'm attaching a test program that I did. I don't especially like the results of it. Maybe it's due to the exponential weighting. (?) The test program tells me that the sum of 3 samples is 8 & average = 2: i.e., not rounded up. And that the sum of 6 samples is 30 & average = 4. (!!) And that the sum of 10 samples is 80 & average = 5. (!!) Am I just not understanding the function or am I misusing it? Complete test program output: > ./moving-avg count: 1, val: 1, sum: 1, avg: 1, internal: 1000 count: 2, val: 3, sum: 4, avg: 2, internal: 2000 count: 3, val: 4, sum: 8, avg: 2, internal: 2666 count: 4, val: 6, sum: 14, avg: 3, internal: 3499 count: 5, val: 7, sum: 21, avg: 4, internal: 4199 count: 6, val: 9, sum: 30, avg: 4, internal: 4999 count: 7, val: 10, sum: 40, avg: 5, internal: 5713 count: 8, val: 12, sum: 52, avg: 6, internal: 6498 count: 9, val: 13, sum: 65, avg: 7, internal: 7220 count: 10, val: 15, sum: 80, avg: 7, internal: 7998 > > -- > Is this the right place to add it? Who to CC:? Try Andrew. (added) > > --- > include/linux/average.h | 32 ++++++++++++++++++++++++++++++++ > 1 files changed, 32 insertions(+), 0 deletions(-) > create mode 100644 include/linux/average.h > > diff --git a/include/linux/average.h b/include/linux/average.h > new file mode 100644 > index 0000000..2a00d3d > --- /dev/null > +++ b/include/linux/average.h > @@ -0,0 +1,32 @@ > +#ifndef _LINUX_AVERAGE_H > +#define _LINUX_AVERAGE_H > + > +#define AVG_FACTOR 1000 > + > +struct avg_val { > + int value; > + int internal; > +}; > + > +/** > + * moving_average - Exponentially weighted moving average > + * @avg: average structure > + * @val: current value > + * @samples: number of samples > + * > + * This implementation make use of a struct avg_val to prevent rounding > + * errors. > + */ > +static inline struct avg_val > +moving_average(const struct avg_val avg, const int val, const int samples) > +{ > + struct avg_val ret; > + ret.internal = avg.internal ? > + (((avg.internal * (samples - 1)) + > + (val * AVG_FACTOR)) / samples) : > + (val * AVG_FACTOR); > + ret.value = ret.internal / AVG_FACTOR; > + return ret; > +} > + > +#endif /* _LINUX_AVERAGE_H */ > > -- --- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code *** [-- Attachment #2: moving-avg.c --] [-- Type: text/plain, Size: 1424 bytes --] #if 0 This adds a generic exponentially weighted moving average function. This implementation makes use of a structure which keeps a scaled up internal representation to reduce rounding errors. The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) and i would like to use it in several places in the mac80211 and ath5k code. #endif #ifndef _LINUX_AVERAGE_H #define _LINUX_AVERAGE_H #define AVG_FACTOR 1000 struct avg_val { int value; int internal; }; /** * moving_average - Exponentially weighted moving average * @avg: average structure * @val: current value * @samples: number of samples * * This implementation make use of a struct avg_val to prevent rounding * errors. */ static inline struct avg_val moving_average(const struct avg_val avg, const int val, const int samples) { struct avg_val ret; ret.internal = avg.internal ? (((avg.internal * (samples - 1)) + (val * AVG_FACTOR)) / samples) : (val * AVG_FACTOR); ret.value = ret.internal / AVG_FACTOR; return ret; } #endif /* _LINUX_AVERAGE_H */ #include <stdio.h> int main(int argc, char *argv[]) { struct avg_val avg = {}; int count; int val; int sum = 0; for (count = 1; count <= 10; count++) { val = count + count/2; sum += val; avg = moving_average(avg, val, count); printf("count: %d, val: %d, sum: %d, avg: %d, internal: %d\n", count, val, sum, avg.value, avg.internal); } return 0; } ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-13 0:33 ` Randy Dunlap @ 2010-10-13 2:10 ` Bruno Randolf 2010-10-13 16:33 ` Randy Dunlap 2010-10-15 3:40 ` Ben Pfaff 1 sibling, 1 reply; 10+ messages in thread From: Bruno Randolf @ 2010-10-13 2:10 UTC (permalink / raw) To: Randy Dunlap; +Cc: linux-kernel, akpm [-- Attachment #1: Type: Text/Plain, Size: 4361 bytes --] Hello Randy! Thank you for taking the time to look at this! On Wed October 13 2010 09:33:52 Randy Dunlap wrote: > On Wed, 06 Oct 2010 18:32:25 +0900 Bruno Randolf wrote: > > This adds a generic exponentially weighted moving average function. This > > implementation makes use of a structure which keeps a scaled up internal > > representation to reduce rounding errors. > > > > The idea for this implementation comes from the rt2x00 driver > > (rt2x00link.c) and i would like to use it in several places in the > > mac80211 and ath5k code. > > > > Signed-off-by: Bruno Randolf <br1@einfach.org> > > I guess I don't understand "exponentially weighted" or why that would > be desirable. Please try to explain (briefly). It just means, that more recent values are given a higher priority. The influence of older values decreases exponentially. This is desirable if we want to have an average, but we are more interested in the recent development. One example where that makes sense is the signal strength of a station in a wireless LAN. As the signal strength can vary significantly for every packet, just looking at one packet is misleading. We are not so much interested in a long term average, as a station might move - we are more interested in the average of the last X (say 8) packets, giving most priority to the last value. http://en.wikipedia.org/wiki/Exponentially_weighted_moving_average#Exponential_moving_average This is one example, but I found several places in the kernel code where something like this is used, usually open coded. That's why I tought a general function for this can make sense. > I'm attaching a test program that I did. I don't especially like the > results of it. Maybe it's due to the exponential weighting. (?) > > The test program tells me that the sum of 3 samples is 8 & average = 2: > i.e., not rounded up. Oh, I see. I guess I should use DIV_ROUND_CLOSEST. I attach a new version of the test program. > And that the sum of 6 samples is 30 & average = 4. (!!) > And that the sum of 10 samples is 80 & average = 5. (!!) > > Am I just not understanding the function or am I misusing it? You should keep 'samples' constant every time you call the function. It is the factor which defines how fast the influence of older values decreases. Higher values will take more older samples into account. E.g. use it like this: moving_average(avg, val, 4); I should have documented that better, I guess... And maybe rename it to 'factor'. Also a moving average makes more sense when you use it more often, depending on how high 'samples' is. E.g. here is the output of the test program with 'sampes' of 4 and 20 iterations. I also print 'savg' (simple average) which is sum/count: count: 1, val: 1, sum: 1, savg: 1, avg: 1, internal: 1000 count: 2, val: 3, sum: 4, savg: 2, avg: 2, internal: 1500 count: 3, val: 4, sum: 8, savg: 2, avg: 2, internal: 2125 count: 4, val: 6, sum: 14, savg: 3, avg: 3, internal: 3093 count: 5, val: 7, sum: 21, savg: 4, avg: 4, internal: 4069 count: 6, val: 9, sum: 30, savg: 5, avg: 5, internal: 5301 count: 7, val: 10, sum: 40, savg: 5, avg: 6, internal: 6475 count: 8, val: 12, sum: 52, savg: 6, avg: 8, internal: 7856 count: 9, val: 13, sum: 65, savg: 7, avg: 9, internal: 9142 count: 10, val: 15, sum: 80, savg: 8, avg: 11, internal: 10606 count: 11, val: 16, sum: 96, savg: 8, avg: 12, internal: 11954 count: 12, val: 18, sum: 114, savg: 9, avg: 13, internal: 13465 count: 13, val: 19, sum: 133, savg: 10, avg: 15, internal: 14848 count: 14, val: 21, sum: 154, savg: 11, avg: 16, internal: 16386 count: 15, val: 22, sum: 176, savg: 11, avg: 18, internal: 17789 count: 16, val: 24, sum: 200, savg: 12, avg: 19, internal: 19341 count: 17, val: 25, sum: 225, savg: 13, avg: 21, internal: 20755 count: 18, val: 27, sum: 252, savg: 14, avg: 22, internal: 22316 count: 19, val: 28, sum: 280, savg: 14, avg: 24, internal: 23737 count: 20, val: 30, sum: 310, savg: 15, avg: 25, internal: 25302 Especially towards the end you can see how a moving average gives preference to the newer values. > > Is this the right place to add it? Who to CC:? > > Try Andrew. (added) I attach a new version of the test program. If you can generally agree that this can be included in the kernel, I'll work on an improved version of my patch. Thank you for your feedback! Bruno [-- Attachment #2: moving-avg.c --] [-- Type: text/x-csrc, Size: 1761 bytes --] #if 0 This adds a generic exponentially weighted moving average function. This implementation makes use of a structure which keeps a scaled up internal representation to reduce rounding errors. The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) and i would like to use it in several places in the mac80211 and ath5k code. #endif #ifndef _LINUX_AVERAGE_H #define _LINUX_AVERAGE_H #define AVG_FACTOR 1000 struct avg_val { int value; int internal; }; #define DIV_ROUND_CLOSEST(x, divisor)( \ { \ typeof(divisor) __divisor = divisor; \ (((x) + ((__divisor) / 2)) / (__divisor)); \ } \ ) /** * moving_average - Exponentially weighted moving average * @avg: average structure * @val: current value * @samples: number of samples. This defines how fast the influence of older * values decreases. Use the same number every time you call this * function for a single avg_val!. * * This implementation make use of a struct avg_val to prevent rounding * errors. */ static inline struct avg_val moving_average(const struct avg_val avg, const int val, const int samples) { struct avg_val ret; ret.internal = avg.internal ? (((avg.internal * (samples - 1)) + (val * AVG_FACTOR)) / samples) : (val * AVG_FACTOR); ret.value = DIV_ROUND_CLOSEST(ret.internal, AVG_FACTOR); return ret; } #endif /* _LINUX_AVERAGE_H */ #include <stdio.h> int main(int argc, char *argv[]) { struct avg_val avg = {}; int count; int val; int sum = 0; for (count = 1; count <= 20; count++) { val = count + count/2; sum += val; avg = moving_average(avg, val, 4); printf("count: %d, val: %d, sum: %d, savg: %d, avg: %d, internal: %d\n", count, val, sum, sum/count, avg.value, avg.internal); } return 0; } ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-13 2:10 ` Bruno Randolf @ 2010-10-13 16:33 ` Randy Dunlap 0 siblings, 0 replies; 10+ messages in thread From: Randy Dunlap @ 2010-10-13 16:33 UTC (permalink / raw) To: Bruno Randolf; +Cc: linux-kernel, akpm On 10/12/10 19:10, Bruno Randolf wrote: > Hello Randy! > > Thank you for taking the time to look at this! > > On Wed October 13 2010 09:33:52 Randy Dunlap wrote: >> On Wed, 06 Oct 2010 18:32:25 +0900 Bruno Randolf wrote: >>> This adds a generic exponentially weighted moving average function. This >>> implementation makes use of a structure which keeps a scaled up internal >>> representation to reduce rounding errors. >>> >>> The idea for this implementation comes from the rt2x00 driver >>> (rt2x00link.c) and i would like to use it in several places in the >>> mac80211 and ath5k code. >>> >>> Signed-off-by: Bruno Randolf <br1@einfach.org> >> >> I guess I don't understand "exponentially weighted" or why that would >> be desirable. Please try to explain (briefly). > > It just means, that more recent values are given a higher priority. The > influence of older values decreases exponentially. Got it, thanks. > This is desirable if we want to have an average, but we are more interested in > the recent development. One example where that makes sense is the signal > strength of a station in a wireless LAN. As the signal strength can vary > significantly for every packet, just looking at one packet is misleading. > We are not so much interested in a long term average, as a station might move > - we are more interested in the average of the last X (say 8) packets, giving > most priority to the last value. > > http://en.wikipedia.org/wiki/Exponentially_weighted_moving_average#Exponential_moving_average > > This is one example, but I found several places in the kernel code where > something like this is used, usually open coded. That's why I tought a general > function for this can make sense. > >> I'm attaching a test program that I did. I don't especially like the >> results of it. Maybe it's due to the exponential weighting. (?) >> >> The test program tells me that the sum of 3 samples is 8 & average = 2: >> i.e., not rounded up. > > Oh, I see. I guess I should use DIV_ROUND_CLOSEST. I attach a new version of > the test program. > >> And that the sum of 6 samples is 30 & average = 4. (!!) >> And that the sum of 10 samples is 80 & average = 5. (!!) >> >> Am I just not understanding the function or am I misusing it? > > You should keep 'samples' constant every time you call the function. It is the > factor which defines how fast the influence of older values decreases. Higher > values will take more older samples into account. E.g. use it like this: > > moving_average(avg, val, 4); > > I should have documented that better, I guess... And maybe rename it to > 'factor'. OK, now I see how it's used. > Also a moving average makes more sense when you use it more often, depending > on how high 'samples' is. E.g. here is the output of the test program with > 'sampes' of 4 and 20 iterations. I also print 'savg' (simple average) which is > sum/count: > > count: 1, val: 1, sum: 1, savg: 1, avg: 1, internal: 1000 > count: 2, val: 3, sum: 4, savg: 2, avg: 2, internal: 1500 > count: 3, val: 4, sum: 8, savg: 2, avg: 2, internal: 2125 > count: 4, val: 6, sum: 14, savg: 3, avg: 3, internal: 3093 > count: 5, val: 7, sum: 21, savg: 4, avg: 4, internal: 4069 > count: 6, val: 9, sum: 30, savg: 5, avg: 5, internal: 5301 > count: 7, val: 10, sum: 40, savg: 5, avg: 6, internal: 6475 > count: 8, val: 12, sum: 52, savg: 6, avg: 8, internal: 7856 > count: 9, val: 13, sum: 65, savg: 7, avg: 9, internal: 9142 > count: 10, val: 15, sum: 80, savg: 8, avg: 11, internal: 10606 > count: 11, val: 16, sum: 96, savg: 8, avg: 12, internal: 11954 > count: 12, val: 18, sum: 114, savg: 9, avg: 13, internal: 13465 > count: 13, val: 19, sum: 133, savg: 10, avg: 15, internal: 14848 > count: 14, val: 21, sum: 154, savg: 11, avg: 16, internal: 16386 > count: 15, val: 22, sum: 176, savg: 11, avg: 18, internal: 17789 > count: 16, val: 24, sum: 200, savg: 12, avg: 19, internal: 19341 > count: 17, val: 25, sum: 225, savg: 13, avg: 21, internal: 20755 > count: 18, val: 27, sum: 252, savg: 14, avg: 22, internal: 22316 > count: 19, val: 28, sum: 280, savg: 14, avg: 24, internal: 23737 > count: 20, val: 30, sum: 310, savg: 15, avg: 25, internal: 25302 > > Especially towards the end you can see how a moving average gives preference > to the newer values. > >>> Is this the right place to add it? Who to CC:? >> >> Try Andrew. (added) > > I attach a new version of the test program. If you can generally agree that > this can be included in the kernel, I'll work on an improved version of my > patch. Seems OK to me as long as there are multiple users of it. > Thank you for your feedback! -- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code *** ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-13 0:33 ` Randy Dunlap 2010-10-13 2:10 ` Bruno Randolf @ 2010-10-15 3:40 ` Ben Pfaff 2010-10-15 14:41 ` Randy Dunlap 1 sibling, 1 reply; 10+ messages in thread From: Ben Pfaff @ 2010-10-15 3:40 UTC (permalink / raw) To: Randy Dunlap; +Cc: Bruno Randolf, linux-kernel, akpm Randy Dunlap <randy.dunlap@oracle.com> writes: > On Wed, 06 Oct 2010 18:32:25 +0900 Bruno Randolf wrote: > >> This adds a generic exponentially weighted moving average function. This >> implementation makes use of a structure which keeps a scaled up internal >> representation to reduce rounding errors. >> >> The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) >> and i would like to use it in several places in the mac80211 and ath5k code. > > I guess I don't understand "exponentially weighted" or why that would > be desirable. Please try to explain (briefly). I wrote up a fairly non-brief explanation of exponentially weighted moving averages a few years ago: http://www.stanford.edu/class/cs140/projects/pintos/pintos_7.html#SEC134 -- Ben Pfaff http://benpfaff.org ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-15 3:40 ` Ben Pfaff @ 2010-10-15 14:41 ` Randy Dunlap 0 siblings, 0 replies; 10+ messages in thread From: Randy Dunlap @ 2010-10-15 14:41 UTC (permalink / raw) To: Ben Pfaff; +Cc: Bruno Randolf, linux-kernel, akpm On 10/14/10 20:40, Ben Pfaff wrote: > Randy Dunlap <randy.dunlap@oracle.com> writes: > >> On Wed, 06 Oct 2010 18:32:25 +0900 Bruno Randolf wrote: >> >>> This adds a generic exponentially weighted moving average function. This >>> implementation makes use of a structure which keeps a scaled up internal >>> representation to reduce rounding errors. >>> >>> The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) >>> and i would like to use it in several places in the mac80211 and ath5k code. >> >> I guess I don't understand "exponentially weighted" or why that would >> be desirable. Please try to explain (briefly). > > I wrote up a fairly non-brief explanation of exponentially > weighted moving averages a few years ago: > http://www.stanford.edu/class/cs140/projects/pintos/pintos_7.html#SEC134 Thanks, nice writeup. -- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code *** ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-06 9:32 [PATCH] Add generic exponentially weighted moving average function Bruno Randolf 2010-10-13 0:33 ` Randy Dunlap @ 2010-10-13 14:01 ` kevin granade 2010-10-14 1:19 ` Bruno Randolf 1 sibling, 1 reply; 10+ messages in thread From: kevin granade @ 2010-10-13 14:01 UTC (permalink / raw) To: Bruno Randolf; +Cc: linux-kernel On Wed, Oct 6, 2010 at 4:32 AM, Bruno Randolf <br1@einfach.org> wrote: > > This adds a generic exponentially weighted moving average function. This > implementation makes use of a structure which keeps a scaled up internal > representation to reduce rounding errors. > > The idea for this implementation comes from the rt2x00 driver (rt2x00link.c) > and i would like to use it in several places in the mac80211 and ath5k code. > > Signed-off-by: Bruno Randolf <br1@einfach.org> > > -- > Is this the right place to add it? Who to CC:? > --- > include/linux/average.h | 32 ++++++++++++++++++++++++++++++++ > 1 files changed, 32 insertions(+), 0 deletions(-) > create mode 100644 include/linux/average.h > > diff --git a/include/linux/average.h b/include/linux/average.h > new file mode 100644 > index 0000000..2a00d3d > --- /dev/null > +++ b/include/linux/average.h > @@ -0,0 +1,32 @@ > +#ifndef _LINUX_AVERAGE_H > +#define _LINUX_AVERAGE_H > + > +#define AVG_FACTOR 1000 If this is going to be general use, wouldn't it be a good feature to make AVG_FACTOR adjustable? > > + > +struct avg_val { > + int value; > + int internal; > +}; This has a scaled up copy of the moving average, which reduces the available range for the average to MAX_INT/(AVG_FACTOR*num_samples) instead of +/- MAX_INT, is that acceptable? Even if it is, shouldn't it be documented? For example, with num_samples = 10, it will roll over to a negative value if the average exceeds 214,748. This seems like a potentially surprising outcome. > > + > +/** > + * moving_average - Exponentially weighted moving average > + * @avg: average structure > + * @val: current value > + * @samples: number of samples > + * > + * This implementation make use of a struct avg_val to prevent rounding > + * errors. > + */ > +static inline struct avg_val > +moving_average(const struct avg_val avg, const int val, const int samples) > +{ > + struct avg_val ret; > + ret.internal = avg.internal ? > + (((avg.internal * (samples - 1)) + > + (val * AVG_FACTOR)) / samples) : I'm not sure what the kernel standard is on this, but is it ok to have this potential div/0 in a general purpose function? Thanks, Kevin Granade > > + (val * AVG_FACTOR); > + ret.value = ret.internal / AVG_FACTOR; > + return ret; > +} > > + > +#endif /* _LINUX_AVERAGE_H */ > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-13 14:01 ` kevin granade @ 2010-10-14 1:19 ` Bruno Randolf 2010-10-15 13:55 ` kevin granade 0 siblings, 1 reply; 10+ messages in thread From: Bruno Randolf @ 2010-10-14 1:19 UTC (permalink / raw) To: kevin granade; +Cc: linux-kernel, Randy Dunlap, akpm On Wed October 13 2010 23:01:16 kevin granade wrote: > On Wed, Oct 6, 2010 at 4:32 AM, Bruno Randolf <br1@einfach.org> wrote: > > This adds a generic exponentially weighted moving average function. This > > implementation makes use of a structure which keeps a scaled up internal > > representation to reduce rounding errors. > > > > The idea for this implementation comes from the rt2x00 driver > > (rt2x00link.c) and i would like to use it in several places in the > > mac80211 and ath5k code. > > > > Signed-off-by: Bruno Randolf <br1@einfach.org> > > > > -- > > Is this the right place to add it? Who to CC:? > > --- > > include/linux/average.h | 32 ++++++++++++++++++++++++++++++++ > > 1 files changed, 32 insertions(+), 0 deletions(-) > > create mode 100644 include/linux/average.h > > > > diff --git a/include/linux/average.h b/include/linux/average.h > > new file mode 100644 > > index 0000000..2a00d3d > > --- /dev/null > > +++ b/include/linux/average.h > > @@ -0,0 +1,32 @@ > > +#ifndef _LINUX_AVERAGE_H > > +#define _LINUX_AVERAGE_H > > + > > +#define AVG_FACTOR 1000 > > If this is going to be general use, wouldn't it be a good feature to > make AVG_FACTOR adjustable? Yes, could be, but how? Another argument to the function call? Actually AVG_FACTOR and 'samples' should stay constant for each struct, so we could also store them in the struct, but that would require an inititalization of the struct before we can use it. > > + > > +struct avg_val { > > + int value; > > + int internal; > > +}; > > This has a scaled up copy of the moving average, which reduces the > available range for the average to MAX_INT/(AVG_FACTOR*num_samples) > instead of +/- MAX_INT, is that acceptable? Even if it is, shouldn't > it be documented? For example, with num_samples = 10, it will roll > over to a negative value if the average exceeds 214,748. This seems > like a potentially surprising outcome. Yes. I'll document this in the next version of the patch. Or should I use 64bit for the internal representation? > > +/** > > + * moving_average - Exponentially weighted moving average > > + * @avg: average structure > > + * @val: current value > > + * @samples: number of samples > > + * > > + * This implementation make use of a struct avg_val to prevent rounding > > + * errors. > > + */ > > +static inline struct avg_val > > +moving_average(const struct avg_val avg, const int val, const int > > samples) +{ > > + struct avg_val ret; > > + ret.internal = avg.internal ? > > + (((avg.internal * (samples - 1)) + > > > + (val * AVG_FACTOR)) / samples) : > I'm not sure what the kernel standard is on this, but is it ok to have > this potential div/0 in a general purpose function? Samples should never be 0. But I can add a WARN_ON... bruno ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-14 1:19 ` Bruno Randolf @ 2010-10-15 13:55 ` kevin granade 2010-10-18 3:27 ` Bruno Randolf 0 siblings, 1 reply; 10+ messages in thread From: kevin granade @ 2010-10-15 13:55 UTC (permalink / raw) To: Bruno Randolf; +Cc: linux-kernel, Randy Dunlap, akpm On Wed, Oct 13, 2010 at 8:19 PM, Bruno Randolf <br1@einfach.org> wrote: > On Wed October 13 2010 23:01:16 kevin granade wrote: >> On Wed, Oct 6, 2010 at 4:32 AM, Bruno Randolf <br1@einfach.org> wrote: >> > This adds a generic exponentially weighted moving average function. This >> > implementation makes use of a structure which keeps a scaled up internal >> > representation to reduce rounding errors. >> > >> > The idea for this implementation comes from the rt2x00 driver >> > (rt2x00link.c) and i would like to use it in several places in the >> > mac80211 and ath5k code. >> > >> > Signed-off-by: Bruno Randolf <br1@einfach.org> >> > >> > -- >> > Is this the right place to add it? Who to CC:? >> > --- >> > include/linux/average.h | 32 ++++++++++++++++++++++++++++++++ >> > 1 files changed, 32 insertions(+), 0 deletions(-) >> > create mode 100644 include/linux/average.h >> > >> > diff --git a/include/linux/average.h b/include/linux/average.h >> > new file mode 100644 >> > index 0000000..2a00d3d >> > --- /dev/null >> > +++ b/include/linux/average.h >> > @@ -0,0 +1,32 @@ >> > +#ifndef _LINUX_AVERAGE_H >> > +#define _LINUX_AVERAGE_H >> > + >> > +#define AVG_FACTOR 1000 >> >> If this is going to be general use, wouldn't it be a good feature to >> make AVG_FACTOR adjustable? > > Yes, could be, but how? Another argument to the function call? > Actually AVG_FACTOR and 'samples' should stay constant for each struct, so we > could also store them in the struct, but that would require an inititalization > of the struct before we can use it. That's true, I can't think of a way of adding this without significantly dirtying the interface. And to be honest, it isn't a, "I might be using this, so it'd be nice to have", but rather a general observation. I guess if someone needs a different decay rate at some point then they can worry about it. > >> > + >> > +struct avg_val { >> > + int value; >> > + int internal; >> > +}; >> >> This has a scaled up copy of the moving average, which reduces the >> available range for the average to MAX_INT/(AVG_FACTOR*num_samples) >> instead of +/- MAX_INT, is that acceptable? Even if it is, shouldn't >> it be documented? For example, with num_samples = 10, it will roll >> over to a negative value if the average exceeds 214,748. This seems >> like a potentially surprising outcome. > > Yes. I'll document this in the next version of the patch. Or should I use > 64bit for the internal representation? If you don't expect the size or speed impact to be significant, it seems like just throwing a bigger number at the problem might be the better option. That will move the rollover to MAX_INT/AVG_FACTOR, unless you also make AVG_FACTOR 64bit, which will promote all of the multiplications to 64bit and provide full MAX_INT range for input and output. > >> > +/** >> > + * moving_average - Exponentially weighted moving average >> > + * @avg: average structure >> > + * @val: current value >> > + * @samples: number of samples >> > + * >> > + * This implementation make use of a struct avg_val to prevent rounding >> > + * errors. >> > + */ >> > +static inline struct avg_val >> > +moving_average(const struct avg_val avg, const int val, const int >> > samples) +{ >> > + struct avg_val ret; >> > + ret.internal = avg.internal ? >> > + (((avg.internal * (samples - 1)) + >> >> > + (val * AVG_FACTOR)) / samples) : >> I'm not sure what the kernel standard is on this, but is it ok to have >> this potential div/0 in a general purpose function? > > Samples should never be 0. But I can add a WARN_ON... > > bruno > I couldn't find anything that clearly indicated what the expected precaution is in this case. It probably isn't an issue now that I understand that samples is intended to remain constant. I initially thought samples would scale from 1 - n as you were initially "loading" samples into the structure, but now I understand that samples remains at n throughout the process. Kevin Granade ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] Add generic exponentially weighted moving average function 2010-10-15 13:55 ` kevin granade @ 2010-10-18 3:27 ` Bruno Randolf 0 siblings, 0 replies; 10+ messages in thread From: Bruno Randolf @ 2010-10-18 3:27 UTC (permalink / raw) To: kevin granade; +Cc: linux-kernel, Randy Dunlap, akpm On Fri October 15 2010 22:55:23 kevin granade wrote: > >> This has a scaled up copy of the moving average, which reduces the > >> available range for the average to MAX_INT/(AVG_FACTOR*num_samples) > >> instead of +/- MAX_INT, is that acceptable? Even if it is, shouldn't > >> it be documented? For example, with num_samples = 10, it will roll > >> over to a negative value if the average exceeds 214,748. This seems > >> like a potentially surprising outcome. > > > > Yes. I'll document this in the next version of the patch. Or should I use > > 64bit for the internal representation? > > If you don't expect the size or speed impact to be significant, it > seems like just throwing a bigger number at the problem might be the > better option. That will move the rollover to MAX_INT/AVG_FACTOR, > unless you also make AVG_FACTOR 64bit, which will promote all of the > multiplications to 64bit and provide full MAX_INT range for input and > output. Honestly, I don't know about the speed impact of using 64 bit vs. 32 bit. I do know however, that the averaging function needs to be called quite often, where I want to use it, so performance could be an issue. And in my case the values are low enough so rollover is not a problem - but obviously I want to make this generally useful. > I couldn't find anything that clearly indicated what the expected > precaution is in this case. It probably isn't an issue now that I > understand that samples is intended to remain constant. I initially > thought samples would scale from 1 - n as you were initially "loading" > samples into the structure, but now I understand that samples remains > at n throughout the process. I will work on the description. Thanks, Bruno ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2010-10-18 3:27 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-10-06 9:32 [PATCH] Add generic exponentially weighted moving average function Bruno Randolf 2010-10-13 0:33 ` Randy Dunlap 2010-10-13 2:10 ` Bruno Randolf 2010-10-13 16:33 ` Randy Dunlap 2010-10-15 3:40 ` Ben Pfaff 2010-10-15 14:41 ` Randy Dunlap 2010-10-13 14:01 ` kevin granade 2010-10-14 1:19 ` Bruno Randolf 2010-10-15 13:55 ` kevin granade 2010-10-18 3:27 ` Bruno Randolf
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.