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