Neal Cardwell | a4f1f9a | 2016-09-19 23:39:09 -0400 | [diff] [blame] | 1 | /** |
| 2 | * lib/minmax.c: windowed min/max tracker |
| 3 | * |
| 4 | * Kathleen Nichols' algorithm for tracking the minimum (or maximum) |
| 5 | * value of a data stream over some fixed time interval. (E.g., |
| 6 | * the minimum RTT over the past five minutes.) It uses constant |
| 7 | * space and constant time per update yet almost always delivers |
| 8 | * the same minimum as an implementation that has to keep all the |
| 9 | * data in the window. |
| 10 | * |
| 11 | * The algorithm keeps track of the best, 2nd best & 3rd best min |
| 12 | * values, maintaining an invariant that the measurement time of |
| 13 | * the n'th best >= n-1'th best. It also makes sure that the three |
| 14 | * values are widely separated in the time window since that bounds |
| 15 | * the worse case error when that data is monotonically increasing |
| 16 | * over the window. |
| 17 | * |
| 18 | * Upon getting a new min, we can forget everything earlier because |
| 19 | * it has no value - the new min is <= everything else in the window |
| 20 | * by definition and it's the most recent. So we restart fresh on |
| 21 | * every new min and overwrites 2nd & 3rd choices. The same property |
| 22 | * holds for 2nd & 3rd best. |
| 23 | */ |
| 24 | #include <linux/module.h> |
| 25 | #include <linux/win_minmax.h> |
| 26 | |
| 27 | /* As time advances, update the 1st, 2nd, and 3rd choices. */ |
| 28 | static u32 minmax_subwin_update(struct minmax *m, u32 win, |
| 29 | const struct minmax_sample *val) |
| 30 | { |
| 31 | u32 dt = val->t - m->s[0].t; |
| 32 | |
| 33 | if (unlikely(dt > win)) { |
| 34 | /* |
| 35 | * Passed entire window without a new val so make 2nd |
| 36 | * choice the new val & 3rd choice the new 2nd choice. |
| 37 | * we may have to iterate this since our 2nd choice |
| 38 | * may also be outside the window (we checked on entry |
| 39 | * that the third choice was in the window). |
| 40 | */ |
| 41 | m->s[0] = m->s[1]; |
| 42 | m->s[1] = m->s[2]; |
| 43 | m->s[2] = *val; |
| 44 | if (unlikely(val->t - m->s[0].t > win)) { |
| 45 | m->s[0] = m->s[1]; |
| 46 | m->s[1] = m->s[2]; |
| 47 | m->s[2] = *val; |
| 48 | } |
| 49 | } else if (unlikely(m->s[1].t == m->s[0].t) && dt > win/4) { |
| 50 | /* |
| 51 | * We've passed a quarter of the window without a new val |
| 52 | * so take a 2nd choice from the 2nd quarter of the window. |
| 53 | */ |
| 54 | m->s[2] = m->s[1] = *val; |
| 55 | } else if (unlikely(m->s[2].t == m->s[1].t) && dt > win/2) { |
| 56 | /* |
| 57 | * We've passed half the window without finding a new val |
| 58 | * so take a 3rd choice from the last half of the window |
| 59 | */ |
| 60 | m->s[2] = *val; |
| 61 | } |
| 62 | return m->s[0].v; |
| 63 | } |
| 64 | |
| 65 | /* Check if new measurement updates the 1st, 2nd or 3rd choice max. */ |
| 66 | u32 minmax_running_max(struct minmax *m, u32 win, u32 t, u32 meas) |
| 67 | { |
| 68 | struct minmax_sample val = { .t = t, .v = meas }; |
| 69 | |
| 70 | if (unlikely(val.v >= m->s[0].v) || /* found new max? */ |
| 71 | unlikely(val.t - m->s[2].t > win)) /* nothing left in window? */ |
| 72 | return minmax_reset(m, t, meas); /* forget earlier samples */ |
| 73 | |
| 74 | if (unlikely(val.v >= m->s[1].v)) |
| 75 | m->s[2] = m->s[1] = val; |
| 76 | else if (unlikely(val.v >= m->s[2].v)) |
| 77 | m->s[2] = val; |
| 78 | |
| 79 | return minmax_subwin_update(m, win, &val); |
| 80 | } |
| 81 | EXPORT_SYMBOL(minmax_running_max); |
| 82 | |
| 83 | /* Check if new measurement updates the 1st, 2nd or 3rd choice min. */ |
| 84 | u32 minmax_running_min(struct minmax *m, u32 win, u32 t, u32 meas) |
| 85 | { |
| 86 | struct minmax_sample val = { .t = t, .v = meas }; |
| 87 | |
| 88 | if (unlikely(val.v <= m->s[0].v) || /* found new min? */ |
| 89 | unlikely(val.t - m->s[2].t > win)) /* nothing left in window? */ |
| 90 | return minmax_reset(m, t, meas); /* forget earlier samples */ |
| 91 | |
| 92 | if (unlikely(val.v <= m->s[1].v)) |
| 93 | m->s[2] = m->s[1] = val; |
| 94 | else if (unlikely(val.v <= m->s[2].v)) |
| 95 | m->s[2] = val; |
| 96 | |
| 97 | return minmax_subwin_update(m, win, &val); |
| 98 | } |