Skip to content

Commit 8e3fdc1

Browse files
committed
WIP: windowed filtered rework+comments (TO BE MERGED)
1 parent 84d6c6b commit 8e3fdc1

File tree

1 file changed

+57
-50
lines changed

1 file changed

+57
-50
lines changed

include/haproxy/window_filter.h

Lines changed: 57 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,102 @@
11
#ifndef _HAPROXY_WINDOW_FILTER_H
22
#define _HAPROXY_WINDOW_FILTER_H
33

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 */
522
struct wf_smp {
623
uint64_t v;
724
uint32_t t;
825
};
926

10-
/* Window filter */
27+
/* Windowed filter */
1128
struct wf {
1229
size_t len;
1330
struct wf_smp smp[3];
1431
};
1532

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)
2337
{
2438
struct wf_smp smp = { .v = v, .t = t };
2539

2640
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);
2752
}
2853

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
3357
*/
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)
3559
{
60+
uint64_t delta_t;
61+
struct wf_smp smp = { .v = v, .t = t };
62+
3663
/* Reset all estimates if they have not yet been initialized, if new
3764
sample is a new best, or if the newest recorded estimate is too
3865
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);
4368

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;
5273

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)) {
5777
wf->smp[0] = wf->smp[1];
5878
wf->smp[1] = wf->smp[2];
5979
wf->smp[2].v = v;
6080
wf->smp[2].t = t;
6181

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)) {
6783
wf->smp[0] = wf->smp[1];
6884
wf->smp[1] = wf->smp[2];
6985
}
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) {
7787
wf->smp[2].v = v;
7888
wf->smp[2].t = t;
7989
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) {
8791
wf->smp[2].v = v;
8892
wf->smp[2].t = t;
8993
}
94+
95+
return wf->smp[0].v;
9096
}
9197

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)
93100
{
94101
return wf->smp[0].v;
95102
}

0 commit comments

Comments
 (0)