lsquic_minmax.c revision 5392f7a3
15392f7a3SLiteSpeed Tech/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc.  See LICENSE. */
25392f7a3SLiteSpeed Tech/*
35392f7a3SLiteSpeed Tech * Based on Google code released under BSD license here:
45392f7a3SLiteSpeed Tech *  https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
55392f7a3SLiteSpeed Tech */
65392f7a3SLiteSpeed Tech
75392f7a3SLiteSpeed Tech/*
85392f7a3SLiteSpeed Tech * Copyright 2017, Google Inc.
95392f7a3SLiteSpeed Tech *
105392f7a3SLiteSpeed Tech * Use of this source code is governed by the following BSD-style license:
115392f7a3SLiteSpeed Tech *
125392f7a3SLiteSpeed Tech * Redistribution and use in source and binary forms, with or without
135392f7a3SLiteSpeed Tech * modification, are permitted provided that the following conditions are
145392f7a3SLiteSpeed Tech * met:
155392f7a3SLiteSpeed Tech *
165392f7a3SLiteSpeed Tech *    * Redistributions of source code must retain the above copyright
175392f7a3SLiteSpeed Tech * notice, this list of conditions and the following disclaimer.
185392f7a3SLiteSpeed Tech *    * Redistributions in binary form must reproduce the above
195392f7a3SLiteSpeed Tech * copyright notice, this list of conditions and the following disclaimer
205392f7a3SLiteSpeed Tech * in the documentation and/or other materials provided with the
215392f7a3SLiteSpeed Tech * distribution.
225392f7a3SLiteSpeed Tech *
235392f7a3SLiteSpeed Tech *    * Neither the name of Google Inc. nor the names of its
245392f7a3SLiteSpeed Tech * contributors may be used to endorse or promote products derived from
255392f7a3SLiteSpeed Tech * this software without specific prior written permission.
265392f7a3SLiteSpeed Tech *
275392f7a3SLiteSpeed Tech * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
285392f7a3SLiteSpeed Tech * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
295392f7a3SLiteSpeed Tech * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
305392f7a3SLiteSpeed Tech * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
315392f7a3SLiteSpeed Tech * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
325392f7a3SLiteSpeed Tech * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
335392f7a3SLiteSpeed Tech * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
345392f7a3SLiteSpeed Tech * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
355392f7a3SLiteSpeed Tech * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
365392f7a3SLiteSpeed Tech * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
375392f7a3SLiteSpeed Tech * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
385392f7a3SLiteSpeed Tech */
395392f7a3SLiteSpeed Tech
405392f7a3SLiteSpeed Tech/*
415392f7a3SLiteSpeed Tech * Kathleen Nichols' algorithm for tracking the minimum (or maximum)
425392f7a3SLiteSpeed Tech * value of a data stream over some fixed time interval.  (E.g.,
435392f7a3SLiteSpeed Tech * the minimum RTT over the past five minutes.) It uses constant
445392f7a3SLiteSpeed Tech * space and constant time per update yet almost always delivers
455392f7a3SLiteSpeed Tech * the same minimum as an implementation that has to keep all the
465392f7a3SLiteSpeed Tech * data in the window.
475392f7a3SLiteSpeed Tech *
485392f7a3SLiteSpeed Tech * The algorithm keeps track of the best, 2nd best & 3rd best min
495392f7a3SLiteSpeed Tech * values, maintaining an invariant that the measurement time of
505392f7a3SLiteSpeed Tech * the n'th best >= n-1'th best. It also makes sure that the three
515392f7a3SLiteSpeed Tech * values are widely separated in the time window since that bounds
525392f7a3SLiteSpeed Tech * the worse case error when that data is monotonically increasing
535392f7a3SLiteSpeed Tech * over the window.
545392f7a3SLiteSpeed Tech *
555392f7a3SLiteSpeed Tech * Upon getting a new min, we can forget everything earlier because
565392f7a3SLiteSpeed Tech * it has no value - the new min is <= everything else in the window
575392f7a3SLiteSpeed Tech * by definition and it'samples the most recent. So we restart fresh on
585392f7a3SLiteSpeed Tech * every new min and overwrites 2nd & 3rd choices. The same property
595392f7a3SLiteSpeed Tech * holds for 2nd & 3rd best.
605392f7a3SLiteSpeed Tech */
615392f7a3SLiteSpeed Tech
625392f7a3SLiteSpeed Tech#include <stdint.h>
635392f7a3SLiteSpeed Tech#include <string.h>
645392f7a3SLiteSpeed Tech
655392f7a3SLiteSpeed Tech#include "lsquic_minmax.h"
665392f7a3SLiteSpeed Tech
675392f7a3SLiteSpeed Tech/* As time advances, update the 1st, 2nd, and 3rd choices. */
685392f7a3SLiteSpeed Techstatic void
695392f7a3SLiteSpeed Techminmax_subwin_update (struct minmax *minmax, const struct minmax_sample *sample)
705392f7a3SLiteSpeed Tech{
715392f7a3SLiteSpeed Tech    uint64_t dt = sample->time - minmax->samples[0].time;
725392f7a3SLiteSpeed Tech
735392f7a3SLiteSpeed Tech    if (dt > minmax->window)
745392f7a3SLiteSpeed Tech    {
755392f7a3SLiteSpeed Tech        /*
765392f7a3SLiteSpeed Tech         * Passed entire window without a new sample so make 2nd
775392f7a3SLiteSpeed Tech         * choice the new sample & 3rd choice the new 2nd choice.
785392f7a3SLiteSpeed Tech         * we may have to iterate this since our 2nd choice
795392f7a3SLiteSpeed Tech         * may also be outside the window (we checked on entry
805392f7a3SLiteSpeed Tech         * that the third choice was in the window).
815392f7a3SLiteSpeed Tech         */
825392f7a3SLiteSpeed Tech        minmax->samples[0] = minmax->samples[1];
835392f7a3SLiteSpeed Tech        minmax->samples[1] = minmax->samples[2];
845392f7a3SLiteSpeed Tech        minmax->samples[2] = *sample;
855392f7a3SLiteSpeed Tech        if (sample->time - minmax->samples[0].time > minmax->window) {
865392f7a3SLiteSpeed Tech            minmax->samples[0] = minmax->samples[1];
875392f7a3SLiteSpeed Tech            minmax->samples[1] = minmax->samples[2];
885392f7a3SLiteSpeed Tech            minmax->samples[2] = *sample;
895392f7a3SLiteSpeed Tech        }
905392f7a3SLiteSpeed Tech    }
915392f7a3SLiteSpeed Tech    else if (minmax->samples[1].time == minmax->samples[0].time
925392f7a3SLiteSpeed Tech                                                && dt > minmax->window / 4)
935392f7a3SLiteSpeed Tech    {
945392f7a3SLiteSpeed Tech        /*
955392f7a3SLiteSpeed Tech         * We've passed a quarter of the window without a new sample
965392f7a3SLiteSpeed Tech         * so take a 2nd choice from the 2nd quarter of the window.
975392f7a3SLiteSpeed Tech         */
985392f7a3SLiteSpeed Tech        minmax->samples[2] = minmax->samples[1] = *sample;
995392f7a3SLiteSpeed Tech    }
1005392f7a3SLiteSpeed Tech    else if (minmax->samples[2].time == minmax->samples[1].time
1015392f7a3SLiteSpeed Tech                                                && dt > minmax->window / 2)
1025392f7a3SLiteSpeed Tech    {
1035392f7a3SLiteSpeed Tech        /*
1045392f7a3SLiteSpeed Tech         * We've passed half the window without finding a new sample
1055392f7a3SLiteSpeed Tech         * so take a 3rd choice from the last half of the window
1065392f7a3SLiteSpeed Tech         */
1075392f7a3SLiteSpeed Tech        minmax->samples[2] = *sample;
1085392f7a3SLiteSpeed Tech    }
1095392f7a3SLiteSpeed Tech}
1105392f7a3SLiteSpeed Tech
1115392f7a3SLiteSpeed Tech
1125392f7a3SLiteSpeed Tech/* Check if new measurement updates the 1st, 2nd or 3rd choice max. */
1135392f7a3SLiteSpeed Techvoid
1145392f7a3SLiteSpeed Techlsquic_minmax_update_max (struct minmax *minmax, uint64_t now, uint64_t meas)
1155392f7a3SLiteSpeed Tech{
1165392f7a3SLiteSpeed Tech    struct minmax_sample sample = { .time = now, .value = meas };
1175392f7a3SLiteSpeed Tech
1185392f7a3SLiteSpeed Tech    if (minmax->samples[0].value == 0                                       /* uninitialized */
1195392f7a3SLiteSpeed Tech            || sample.value >= minmax->samples[0].value                     /* found new max? */
1205392f7a3SLiteSpeed Tech            || sample.time - minmax->samples[2].time > minmax->window)      /* nothing left in window? */
1215392f7a3SLiteSpeed Tech    {
1225392f7a3SLiteSpeed Tech        minmax_reset(minmax, sample);  /* forget earlier samples */
1235392f7a3SLiteSpeed Tech        return;
1245392f7a3SLiteSpeed Tech    }
1255392f7a3SLiteSpeed Tech
1265392f7a3SLiteSpeed Tech    if (sample.value >= minmax->samples[1].value)
1275392f7a3SLiteSpeed Tech        minmax->samples[2] = minmax->samples[1] = sample;
1285392f7a3SLiteSpeed Tech    else if (sample.value >= minmax->samples[2].value)
1295392f7a3SLiteSpeed Tech        minmax->samples[2] = sample;
1305392f7a3SLiteSpeed Tech
1315392f7a3SLiteSpeed Tech    minmax_subwin_update(minmax, &sample);
1325392f7a3SLiteSpeed Tech}
1335392f7a3SLiteSpeed Tech
1345392f7a3SLiteSpeed Tech
1355392f7a3SLiteSpeed Tech/* Check if new measurement updates the 1st, 2nd or 3rd choice min. */
1365392f7a3SLiteSpeed Techvoid
1375392f7a3SLiteSpeed Techlsquic_minmax_update_min (struct minmax *minmax, uint64_t now, uint64_t meas)
1385392f7a3SLiteSpeed Tech{
1395392f7a3SLiteSpeed Tech    struct minmax_sample sample = { .time = now, .value = meas };
1405392f7a3SLiteSpeed Tech
1415392f7a3SLiteSpeed Tech    if (minmax->samples[0].value == 0                                       /* uninitialized */
1425392f7a3SLiteSpeed Tech            || sample.value <= minmax->samples[0].value                     /* found new min? */
1435392f7a3SLiteSpeed Tech            || sample.time - minmax->samples[2].time > minmax->window)      /* nothing left in window? */
1445392f7a3SLiteSpeed Tech    {
1455392f7a3SLiteSpeed Tech        minmax_reset(minmax, sample);  /* forget earlier samples */
1465392f7a3SLiteSpeed Tech        return;
1475392f7a3SLiteSpeed Tech    }
1485392f7a3SLiteSpeed Tech
1495392f7a3SLiteSpeed Tech    if (sample.value <= minmax->samples[1].value)
1505392f7a3SLiteSpeed Tech        minmax->samples[2] = minmax->samples[1] = sample;
1515392f7a3SLiteSpeed Tech    else if (sample.value <= minmax->samples[2].value)
1525392f7a3SLiteSpeed Tech        minmax->samples[2] = sample;
1535392f7a3SLiteSpeed Tech
1545392f7a3SLiteSpeed Tech    minmax_subwin_update(minmax, &sample);
1555392f7a3SLiteSpeed Tech}
156