From 627505d36a1ef9a34eeeb22965cfff44540a1c05 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 17 Oct 2018 09:24:56 +0200 Subject: [PATCH] MINOR: freq_ctr: add swrate_add_scaled() to work with large samples Some samples representing time will cover more than one sample at once if they are units of time per time. For this we'd need to have the ability to loop over swrate_add() multiple times but that would be inefficient. By developing the function elevated to power N, it's visible that some coefficients quickly disappear and that those which remain at the first order more or less compensate each other. Thus a simplified version of this function was added to provide a single value for a given number of samples. Tests with multiple values, window sizes and sample sizes have shown that it is possible to make it remain surprisingly accurate (typical error < 0.2% over various large window and sample sizes, even samples representing up to 1/4 of the window). --- include/proto/freq_ctr.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/proto/freq_ctr.h b/include/proto/freq_ctr.h index c10ddf2aa..277bfda5a 100644 --- a/include/proto/freq_ctr.h +++ b/include/proto/freq_ctr.h @@ -244,6 +244,33 @@ static inline unsigned int swrate_add(unsigned int *sum, unsigned int n, unsigne return *sum = *sum - (*sum + n - 1) / n + v; } +/* Adds sample value spanning samples to sliding window sum + * configured for samples, where is supposed to be "much larger" than + * . The sample is returned. Better if is a power of two. Note that this + * is only an approximate. Indeed, as can be seen with two samples only over a + * 8-sample window, the original function would return : + * sum1 = sum - (sum + 7) / 8 + v + * sum2 = sum1 - (sum1 + 7) / 8 + v + * = (sum - (sum + 7) / 8 + v) - (sum - (sum + 7) / 8 + v + 7) / 8 + v + * ~= 7sum/8 - 7/8 + v - sum/8 + sum/64 - 7/64 - v/8 - 7/8 + v + * ~= (3sum/4 + sum/64) - (7/4 + 7/64) + 15v/8 + * + * while the function below would return : + * sum = sum + 2*v - (sum + 8) * 2 / 8 + * = 3sum/4 + 2v - 2 + * + * this presents an error of ~ (sum/64 + 9/64 + v/8) = (sum+n+1)/(n^s) + v/n + * + * Thus the simplified function effectively replaces a part of the history with + * a linear sum instead of applying the exponential one. But as long as s/n is + * "small enough", the error fades away and remains small for both small and + * large values of n and s (typically < 0.2% measured). + */ +static inline unsigned int swrate_add_scaled(unsigned int *sum, unsigned int n, unsigned int v, unsigned int s) +{ + return *sum = *sum + v * s - div64_32((unsigned long long)(*sum + n) * s, n); +} + /* Returns the average sample value for the sum over a sliding window of * samples. Better if is a power of two. It must be the same as the * one used above in all additions.