|
1 | 1 | #ifndef _HAPROXY_WINDOW_FILTER_H |
2 | 2 | #define _HAPROXY_WINDOW_FILTER_H |
3 | 3 |
|
4 | | -/* Window filter sample */ |
| 4 | +/* Kathleen Nichols' algorithm to track the maximum value of a data type during |
| 5 | + * a fixed time interval. This algorithm makes usage of three samples to track |
| 6 | + * the best, second best and third best values with 1st >= 2nd >= 3rd as |
| 7 | + * invariant. |
| 8 | + * |
| 9 | + * This code is used in Linux kernel in linux/win_minmax.c to track both |
| 10 | + * minimal and maximum values. |
| 11 | + * |
| 12 | + * Here the code has been adapted to track 64bits values and only their |
| 13 | + * maximum. |
| 14 | + * |
| 15 | + * Note that these windowed filters are used by BBR to filter the maximum |
| 16 | + * estimated bandwidth with counters as time values. A length has been |
| 17 | + * added to simulate the fixed time interval with counter which are |
| 18 | + * monotonically increasing. |
| 19 | + */ |
| 20 | + |
| 21 | +/* Windowed filter sample */ |
5 | 22 | struct wf_smp { |
6 | 23 | uint64_t v; |
7 | 24 | uint32_t t; |
8 | 25 | }; |
9 | 26 |
|
10 | | -/* Window filter */ |
| 27 | +/* Windowed filter */ |
11 | 28 | struct wf { |
12 | 29 | size_t len; |
13 | 30 | struct wf_smp smp[3]; |
14 | 31 | }; |
15 | 32 |
|
16 | | -static inline void wf_init(struct wf *wf, size_t len) |
17 | | -{ |
18 | | - wf->len = len; |
19 | | - memset(wf->smp, 0xff, sizeof(wf->smp)); |
20 | | -} |
21 | | - |
22 | | -static inline void wf_reset(struct wf *wf, uint64_t v, uint32_t t) |
| 33 | +/* Reset all the <wf> windowed filter samples with <v> and <t> as value and |
| 34 | + * time value respectively. |
| 35 | + */ |
| 36 | +static inline uint64_t wf_reset(struct wf *wf, uint64_t v, uint32_t t) |
23 | 37 | { |
24 | 38 | struct wf_smp smp = { .v = v, .t = t }; |
25 | 39 |
|
26 | 40 | wf->smp[2] = wf->smp[1] = wf->smp[0] = smp; |
| 41 | + |
| 42 | + return wf->smp[0].v; |
| 43 | +} |
| 44 | + |
| 45 | +/* Initialize <wf> windowed filter to track maximum values, with <len> as |
| 46 | + * length and <v> and <t> as value and time value respectively. |
| 47 | + */ |
| 48 | +static inline void wf_init(struct wf *wf, size_t len, uint64_t v, uint32_t t) |
| 49 | +{ |
| 50 | + wf->len = len; |
| 51 | + wf_reset(wf, v, t); |
27 | 52 | } |
28 | 53 |
|
29 | | -/* Updates best estimates with |v| sample value, and expires and updates best |
30 | | - * estimates as necessary. |
31 | | - * Similar to minmax_subwin_update() linux kernel function (see lib/win_minmax.c |
32 | | - * function). |
| 54 | +/* Similar to minmax_running_max() Linux kernel function to update the best |
| 55 | + * estimation of <wf> windowed filted with <v> and <t> as value and time value |
| 56 | + * respectively |
33 | 57 | */ |
34 | | -static inline void wf_update(struct wf *wf, uint64_t v, uint32_t t) |
| 58 | +static inline uint64_t wf_max_update(struct wf *wf, uint64_t v, uint32_t t) |
35 | 59 | { |
| 60 | + uint64_t delta_t; |
| 61 | + struct wf_smp smp = { .v = v, .t = t }; |
| 62 | + |
36 | 63 | /* Reset all estimates if they have not yet been initialized, if new |
37 | 64 | sample is a new best, or if the newest recorded estimate is too |
38 | 65 | old. */ |
39 | | - if (wf->smp[0].v == UINT64_MAX || v > wf->smp[0].v || t - wf->smp[2].t > wf->len) { |
40 | | - wf_reset(wf, v, t); |
41 | | - return; |
42 | | - } |
| 66 | + if (unlikely(v > wf->smp[0].v) || unlikely(t - wf->smp[2].t > wf->len)) |
| 67 | + return wf_reset(wf, v, t); |
43 | 68 |
|
44 | | - if (v > wf->smp[1].v) { |
45 | | - wf->smp[1].v = v; |
46 | | - wf->smp[1].t = t; |
47 | | - wf->smp[2] = wf->smp[1]; |
48 | | - } else if (v > wf->smp[2].v) { |
49 | | - wf->smp[2].v = v; |
50 | | - wf->smp[2].t = t; |
51 | | - } |
| 69 | + if (unlikely(v > wf->smp[1].v)) |
| 70 | + wf->smp[2] = wf->smp[1] = smp; |
| 71 | + else if (unlikely(v > wf->smp[2].v)) |
| 72 | + wf->smp[2] = smp; |
52 | 73 |
|
53 | | - /* Expire and update smp as necessary. */ |
54 | | - if (t - wf->smp[0].t > wf->len) { |
55 | | - /* The best estimate hasn't been updated for an entire window, so |
56 | | - promote second and third best smp. */ |
| 74 | + delta_t = t - wf->smp[0].t; |
| 75 | + /* From here, similar to minmax_subwin_update() from Linux kernel. */ |
| 76 | + if (unlikely(delta_t > wf->len)) { |
57 | 77 | wf->smp[0] = wf->smp[1]; |
58 | 78 | wf->smp[1] = wf->smp[2]; |
59 | 79 | wf->smp[2].v = v; |
60 | 80 | wf->smp[2].t = t; |
61 | 81 |
|
62 | | - /* Need to iterate one more time. Check if the new best estimate |
63 | | - is outside the window as well, since it may also have been |
64 | | - recorded a long time ago. Don't need to iterate once more |
65 | | - since we cover that case at the beginning of the method. */ |
66 | | - if (t - wf->smp[0].t > wf->len) { |
| 82 | + if (unlikely(t - wf->smp[0].t > wf->len)) { |
67 | 83 | wf->smp[0] = wf->smp[1]; |
68 | 84 | wf->smp[1] = wf->smp[2]; |
69 | 85 | } |
70 | | - return; |
71 | | - } |
72 | | - |
73 | | - if (wf->smp[1].v == wf->smp[0].v && t - wf->smp[1].t > wf->len >> 2) { |
74 | | - /* A quarter of the window has passed without a better sample, so |
75 | | - the second-best estimate is taken from the second quarter of |
76 | | - the window. */ |
| 86 | + } else if (unlikely(wf->smp[1].v == wf->smp[0].v) && delta_t > wf->len / 4) { |
77 | 87 | wf->smp[2].v = v; |
78 | 88 | wf->smp[2].t = t; |
79 | 89 | wf->smp[1] = wf->smp[2]; |
80 | | - return; |
81 | | - } |
82 | | - |
83 | | - if (wf->smp[2].v == wf->smp[1].v && t - wf->smp[2].t > wf->len >> 1) { |
84 | | - /* We've passed a half of the window without a better estimate, so |
85 | | - take a third-best estimate from the second half of the |
86 | | - window. */ |
| 90 | + } else if (unlikely(wf->smp[2].v == wf->smp[1].v) && delta_t > wf->len / 2) { |
87 | 91 | wf->smp[2].v = v; |
88 | 92 | wf->smp[2].t = t; |
89 | 93 | } |
| 94 | + |
| 95 | + return wf->smp[0].v; |
90 | 96 | } |
91 | 97 |
|
92 | | -static inline uint64_t wf_get_best(struct wf *wf) |
| 98 | +/* Return <wf> windowed filter best maximum estimation. */ |
| 99 | +static inline uint64_t wf_get_max(struct wf *wf) |
93 | 100 | { |
94 | 101 | return wf->smp[0].v; |
95 | 102 | } |
|
0 commit comments