MINOR: window_filter: Implement windowed filter (only max)
Implement the Kathleen Nichols' algorithm used by several congestion control algorithm implementation (TCP/BBR in Linux kernel, QUIC/BBR in quiche) to track the maximum value of a data type during a fixe time interval. In this implementation, counters which are periodically reset are used in place of timestamps. Only the max part has been implemented. (see lib/minmax.c implemenation for Linux kernel).
This commit is contained in:
parent
7bbe8828ba
commit
c08b877657
|
@ -0,0 +1,104 @@
|
||||||
|
#ifndef _HAPROXY_WINDOW_FILTER_H
|
||||||
|
#define _HAPROXY_WINDOW_FILTER_H
|
||||||
|
|
||||||
|
/* Kathleen Nichols' algorithm to track the maximum values of a data type during
|
||||||
|
* a fixed time interval. This algorithm makes usage of three samples to track
|
||||||
|
* the best, second best and third best values with 1st >= 2nd >= 3rd as
|
||||||
|
* invariant.
|
||||||
|
*
|
||||||
|
* This code is used in Linux kernel in linux/win_minmax.c to track both
|
||||||
|
* minimal and maximum values.
|
||||||
|
*
|
||||||
|
* Here the code has been adapted to track 64 bits values and only their
|
||||||
|
* maximum.
|
||||||
|
*
|
||||||
|
* Note that these windowed filters are used by BBR to filter the maximum
|
||||||
|
* estimated bandwidth with counters as time values. A length has been
|
||||||
|
* added to simulate the fixed time interval with counter which are
|
||||||
|
* monotonically increasing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Windowed filter sample */
|
||||||
|
struct wf_smp {
|
||||||
|
uint64_t v;
|
||||||
|
uint32_t t;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Windowed filter */
|
||||||
|
struct wf {
|
||||||
|
size_t len;
|
||||||
|
struct wf_smp smp[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Reset all the <wf> windowed filter samples with <v> and <t> as value and
|
||||||
|
* time value respectively.
|
||||||
|
*/
|
||||||
|
static inline uint64_t wf_reset(struct wf *wf, uint64_t v, uint32_t t)
|
||||||
|
{
|
||||||
|
struct wf_smp smp = { .v = v, .t = t };
|
||||||
|
|
||||||
|
wf->smp[2] = wf->smp[1] = wf->smp[0] = smp;
|
||||||
|
|
||||||
|
return wf->smp[0].v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize <wf> windowed filter to track maximum values, with <len> as
|
||||||
|
* length and <v> and <t> as value and time value respectively.
|
||||||
|
*/
|
||||||
|
static inline void wf_init(struct wf *wf, size_t len, uint64_t v, uint32_t t)
|
||||||
|
{
|
||||||
|
wf->len = len;
|
||||||
|
wf_reset(wf, v, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Similar to minmax_running_max() Linux kernel function to update the best
|
||||||
|
* estimation of <wf> windowed filted with <v> and <t> as value and time value
|
||||||
|
* respectively
|
||||||
|
*/
|
||||||
|
static inline uint64_t wf_max_update(struct wf *wf, uint64_t v, uint32_t t)
|
||||||
|
{
|
||||||
|
uint64_t delta_t;
|
||||||
|
struct wf_smp smp = { .v = v, .t = t };
|
||||||
|
|
||||||
|
/* Reset all estimates if they have not yet been initialized, if new
|
||||||
|
sample is a new best, or if the newest recorded estimate is too
|
||||||
|
old. */
|
||||||
|
if (unlikely(v > wf->smp[0].v) || unlikely(t - wf->smp[2].t > wf->len))
|
||||||
|
return wf_reset(wf, v, t);
|
||||||
|
|
||||||
|
if (unlikely(v > wf->smp[1].v))
|
||||||
|
wf->smp[2] = wf->smp[1] = smp;
|
||||||
|
else if (unlikely(v > wf->smp[2].v))
|
||||||
|
wf->smp[2] = smp;
|
||||||
|
|
||||||
|
delta_t = t - wf->smp[0].t;
|
||||||
|
/* From here, similar to minmax_subwin_update() from Linux kernel. */
|
||||||
|
if (unlikely(delta_t > wf->len)) {
|
||||||
|
wf->smp[0] = wf->smp[1];
|
||||||
|
wf->smp[1] = wf->smp[2];
|
||||||
|
wf->smp[2].v = v;
|
||||||
|
wf->smp[2].t = t;
|
||||||
|
|
||||||
|
if (unlikely(t - wf->smp[0].t > wf->len)) {
|
||||||
|
wf->smp[0] = wf->smp[1];
|
||||||
|
wf->smp[1] = wf->smp[2];
|
||||||
|
}
|
||||||
|
} else if (unlikely(wf->smp[1].v == wf->smp[0].v) && delta_t > wf->len / 4) {
|
||||||
|
wf->smp[2].v = v;
|
||||||
|
wf->smp[2].t = t;
|
||||||
|
wf->smp[1] = wf->smp[2];
|
||||||
|
} else if (unlikely(wf->smp[2].v == wf->smp[1].v) && delta_t > wf->len / 2) {
|
||||||
|
wf->smp[2].v = v;
|
||||||
|
wf->smp[2].t = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wf->smp[0].v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return <wf> windowed filter best maximum estimation. */
|
||||||
|
static inline uint64_t wf_get_max(struct wf *wf)
|
||||||
|
{
|
||||||
|
return wf->smp[0].v;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _HAPROXY_WINDOW_FILTER_H */
|
Loading…
Reference in New Issue