• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // Copyright (C) 2020, Cloudflare, Inc.
2  // Copyright (C) 2017, Google, Inc.
3  //
4  // Use of this source code is governed by the following BSD-style license:
5  //
6  // Redistribution and use in source and binary forms, with or without
7  // modification, are permitted provided that the following conditions are
8  // met:
9  //
10  //    * Redistributions of source code must retain the above copyright
11  // notice, this list of conditions and the following disclaimer.
12  //    * Redistributions in binary form must reproduce the above
13  // copyright notice, this list of conditions and the following disclaimer
14  // in the documentation and/or other materials provided with the
15  // distribution.
16  //
17  //    * Neither the name of Google Inc. nor the names of its
18  // contributors may be used to endorse or promote products derived from
19  // this software without specific prior written permission.
20  //
21  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  
33  // lib/minmax.c: windowed min/max tracker
34  //
35  // Kathleen Nichols' algorithm for tracking the minimum (or maximum)
36  // value of a data stream over some fixed time interval.  (E.g.,
37  // the minimum RTT over the past five minutes.) It uses constant
38  // space and constant time per update yet almost always delivers
39  // the same minimum as an implementation that has to keep all the
40  // data in the window.
41  //
42  // The algorithm keeps track of the best, 2nd best & 3rd best min
43  // values, maintaining an invariant that the measurement time of
44  // the n'th best >= n-1'th best. It also makes sure that the three
45  // values are widely separated in the time window since that bounds
46  // the worse case error when that data is monotonically increasing
47  // over the window.
48  //
49  // Upon getting a new min, we can forget everything earlier because
50  // it has no value - the new min is <= everything else in the window
51  // by definition and it's the most recent. So we restart fresh on
52  // every new min and overwrites 2nd & 3rd choices. The same property
53  // holds for 2nd & 3rd best.
54  
55  use std::time::Duration;
56  use std::time::Instant;
57  
58  #[derive(Copy, Clone)]
59  struct MinmaxSample<T> {
60      time: Instant,
61      value: T,
62  }
63  
64  pub struct Minmax<T> {
65      estimate: [MinmaxSample<T>; 3],
66  }
67  
68  impl<T: PartialOrd + Copy> Minmax<T> {
new(val: T) -> Self69      pub fn new(val: T) -> Self {
70          Minmax {
71              estimate: [MinmaxSample {
72                  time: Instant::now(),
73                  value: val,
74              }; 3],
75          }
76      }
77  
78      /// Resets the estimates to the given value.
reset(&mut self, time: Instant, meas: T) -> T79      pub fn reset(&mut self, time: Instant, meas: T) -> T {
80          let val = MinmaxSample { time, value: meas };
81  
82          for i in self.estimate.iter_mut() {
83              *i = val;
84          }
85  
86          self.estimate[0].value
87      }
88  
89      /// Updates the min estimate based on the given measurement, and returns it.
running_min(&mut self, win: Duration, time: Instant, meas: T) -> T90      pub fn running_min(&mut self, win: Duration, time: Instant, meas: T) -> T {
91          let val = MinmaxSample { time, value: meas };
92  
93          let delta_time = time.duration_since(self.estimate[2].time);
94  
95          // Reset if there's nothing in the window or a new min value is found.
96          if val.value <= self.estimate[0].value || delta_time > win {
97              return self.reset(time, meas);
98          }
99  
100          if val.value <= self.estimate[1].value {
101              self.estimate[2] = val;
102              self.estimate[1] = val;
103          } else if val.value <= self.estimate[2].value {
104              self.estimate[2] = val;
105          }
106  
107          self.subwin_update(win, time, meas)
108      }
109  
110      /// Updates the max estimate based on the given measurement, and returns it.
_running_max(&mut self, win: Duration, time: Instant, meas: T) -> T111      pub fn _running_max(&mut self, win: Duration, time: Instant, meas: T) -> T {
112          let val = MinmaxSample { time, value: meas };
113  
114          let delta_time = time.duration_since(self.estimate[2].time);
115  
116          // Reset if there's nothing in the window or a new max value is found.
117          if val.value >= self.estimate[0].value || delta_time > win {
118              return self.reset(time, meas);
119          }
120  
121          if val.value >= self.estimate[1].value {
122              self.estimate[2] = val;
123              self.estimate[1] = val;
124          } else if val.value >= self.estimate[2].value {
125              self.estimate[2] = val
126          }
127  
128          self.subwin_update(win, time, meas)
129      }
130  
131      /// As time advances, update the 1st, 2nd and 3rd estimates.
subwin_update(&mut self, win: Duration, time: Instant, meas: T) -> T132      fn subwin_update(&mut self, win: Duration, time: Instant, meas: T) -> T {
133          let val = MinmaxSample { time, value: meas };
134  
135          let delta_time = time.duration_since(self.estimate[0].time);
136  
137          if delta_time > win {
138              // Passed entire window without a new val so make 2nd estimate the
139              // new val & 3rd estimate the new 2nd choice. we may have to iterate
140              // this since our 2nd estimate may also be outside the window (we
141              // checked on entry that the third estimate was in the window).
142              self.estimate[0] = self.estimate[1];
143              self.estimate[1] = self.estimate[2];
144              self.estimate[2] = val;
145  
146              if time.duration_since(self.estimate[0].time) > win {
147                  self.estimate[0] = self.estimate[1];
148                  self.estimate[1] = self.estimate[2];
149                  self.estimate[2] = val;
150              }
151          } else if self.estimate[1].time == self.estimate[0].time &&
152              delta_time > win.div_f32(4.0)
153          {
154              // We've passed a quarter of the window without a new val so take a
155              // 2nd estimate from the 2nd quarter of the window.
156              self.estimate[2] = val;
157              self.estimate[1] = val;
158          } else if self.estimate[2].time == self.estimate[1].time &&
159              delta_time > win.div_f32(2.0)
160          {
161              // We've passed half the window without finding a new val so take a
162              // 3rd estimate from the last half of the window.
163              self.estimate[2] = val;
164          }
165  
166          self.estimate[0].value
167      }
168  }
169  
170  #[cfg(test)]
171  mod tests {
172      use super::*;
173  
174      #[test]
reset_filter_rtt()175      fn reset_filter_rtt() {
176          let mut f = Minmax::new(Duration::new(0, 0));
177          let now = Instant::now();
178          let rtt = Duration::from_millis(50);
179  
180          let rtt_min = f.reset(now, rtt);
181          assert_eq!(rtt_min, rtt);
182  
183          assert_eq!(f.estimate[0].time, now);
184          assert_eq!(f.estimate[0].value, rtt);
185  
186          assert_eq!(f.estimate[1].time, now);
187          assert_eq!(f.estimate[1].value, rtt);
188  
189          assert_eq!(f.estimate[2].time, now);
190          assert_eq!(f.estimate[2].value, rtt);
191      }
192  
193      #[test]
reset_filter_bandwidth()194      fn reset_filter_bandwidth() {
195          let mut f = Minmax::new(0);
196          let now = Instant::now();
197          let bw = 2000;
198  
199          let bw_min = f.reset(now, bw);
200          assert_eq!(bw_min, bw);
201  
202          assert_eq!(f.estimate[0].time, now);
203          assert_eq!(f.estimate[0].value, bw);
204  
205          assert_eq!(f.estimate[1].time, now);
206          assert_eq!(f.estimate[1].value, bw);
207  
208          assert_eq!(f.estimate[2].time, now);
209          assert_eq!(f.estimate[2].value, bw);
210      }
211  
212      #[test]
get_windowed_min_rtt()213      fn get_windowed_min_rtt() {
214          let mut f = Minmax::new(Duration::new(0, 0));
215          let rtt_25 = Duration::from_millis(25);
216          let rtt_24 = Duration::from_millis(24);
217          let win = Duration::from_millis(500);
218          let mut time = Instant::now();
219  
220          let mut rtt_min = f.reset(time, rtt_25);
221          assert_eq!(rtt_min, rtt_25);
222  
223          time += Duration::from_millis(250);
224          rtt_min = f.running_min(win, time, rtt_24);
225          assert_eq!(rtt_min, rtt_24);
226          assert_eq!(f.estimate[1].value, rtt_24);
227          assert_eq!(f.estimate[2].value, rtt_24);
228  
229          time += Duration::from_millis(600);
230          rtt_min = f.running_min(win, time, rtt_25);
231          assert_eq!(rtt_min, rtt_25);
232          assert_eq!(f.estimate[1].value, rtt_25);
233          assert_eq!(f.estimate[2].value, rtt_25);
234      }
235  
236      #[test]
get_windowed_min_bandwidth()237      fn get_windowed_min_bandwidth() {
238          let mut f = Minmax::new(0);
239          let bw_200 = 200;
240          let bw_500 = 500;
241          let win = Duration::from_millis(500);
242          let mut time = Instant::now();
243  
244          let mut bw_min = f.reset(time, bw_500);
245          assert_eq!(bw_min, bw_500);
246  
247          time += Duration::from_millis(250);
248          bw_min = f.running_min(win, time, bw_200);
249          assert_eq!(bw_min, bw_200);
250          assert_eq!(f.estimate[1].value, bw_200);
251          assert_eq!(f.estimate[2].value, bw_200);
252  
253          time += Duration::from_millis(600);
254          bw_min = f.running_min(win, time, bw_500);
255          assert_eq!(bw_min, bw_500);
256          assert_eq!(f.estimate[1].value, bw_500);
257          assert_eq!(f.estimate[2].value, bw_500);
258      }
259  
260      #[test]
get_windowed_max_rtt()261      fn get_windowed_max_rtt() {
262          let mut f = Minmax::new(Duration::new(0, 0));
263          let rtt_25 = Duration::from_millis(25);
264          let rtt_24 = Duration::from_millis(24);
265          let win = Duration::from_millis(500);
266          let mut time = Instant::now();
267  
268          let mut rtt_max = f.reset(time, rtt_24);
269          assert_eq!(rtt_max, rtt_24);
270  
271          time += Duration::from_millis(250);
272          rtt_max = f._running_max(win, time, rtt_25);
273          assert_eq!(rtt_max, rtt_25);
274          assert_eq!(f.estimate[1].value, rtt_25);
275          assert_eq!(f.estimate[2].value, rtt_25);
276  
277          time += Duration::from_millis(600);
278          rtt_max = f._running_max(win, time, rtt_24);
279          assert_eq!(rtt_max, rtt_24);
280          assert_eq!(f.estimate[1].value, rtt_24);
281          assert_eq!(f.estimate[2].value, rtt_24);
282      }
283  
284      #[test]
get_windowed_max_bandwidth()285      fn get_windowed_max_bandwidth() {
286          let mut f = Minmax::new(0);
287          let bw_200 = 200;
288          let bw_500 = 500;
289          let win = Duration::from_millis(500);
290          let mut time = Instant::now();
291  
292          let mut bw_max = f.reset(time, bw_200);
293          assert_eq!(bw_max, bw_200);
294  
295          time += Duration::from_millis(5000);
296          bw_max = f._running_max(win, time, bw_500);
297          assert_eq!(bw_max, bw_500);
298          assert_eq!(f.estimate[1].value, bw_500);
299          assert_eq!(f.estimate[2].value, bw_500);
300  
301          time += Duration::from_millis(600);
302          bw_max = f._running_max(win, time, bw_200);
303          assert_eq!(bw_max, bw_200);
304          assert_eq!(f.estimate[1].value, bw_200);
305          assert_eq!(f.estimate[2].value, bw_200);
306      }
307  
308      #[test]
get_windowed_min_estimates_rtt()309      fn get_windowed_min_estimates_rtt() {
310          let mut f = Minmax::new(Duration::new(0, 0));
311          let rtt_25 = Duration::from_millis(25);
312          let rtt_24 = Duration::from_millis(24);
313          let rtt_23 = Duration::from_millis(23);
314          let rtt_22 = Duration::from_millis(22);
315          let win = Duration::from_secs(1);
316          let mut time = Instant::now();
317  
318          let mut rtt_min = f.reset(time, rtt_23);
319          assert_eq!(rtt_min, rtt_23);
320  
321          time += Duration::from_millis(300);
322          rtt_min = f.running_min(win, time, rtt_24);
323          assert_eq!(rtt_min, rtt_23);
324          assert_eq!(f.estimate[1].value, rtt_24);
325          assert_eq!(f.estimate[2].value, rtt_24);
326  
327          time += Duration::from_millis(300);
328          rtt_min = f.running_min(win, time, rtt_25);
329          assert_eq!(rtt_min, rtt_23);
330          assert_eq!(f.estimate[1].value, rtt_24);
331          assert_eq!(f.estimate[2].value, rtt_25);
332  
333          time += Duration::from_millis(300);
334          rtt_min = f.running_min(win, time, rtt_22);
335          assert_eq!(rtt_min, rtt_22);
336          assert_eq!(f.estimate[1].value, rtt_22);
337          assert_eq!(f.estimate[2].value, rtt_22);
338      }
339  
340      #[test]
get_windowed_min_estimates_bandwidth()341      fn get_windowed_min_estimates_bandwidth() {
342          let mut f = Minmax::new(0);
343          let bw_500 = 500;
344          let bw_400 = 400;
345          let bw_300 = 300;
346          let bw_200 = 200;
347          let win = Duration::from_secs(1);
348          let mut time = Instant::now();
349  
350          let mut bw_min = f.reset(time, bw_300);
351          assert_eq!(bw_min, bw_300);
352  
353          time += Duration::from_millis(300);
354          bw_min = f.running_min(win, time, bw_400);
355          assert_eq!(bw_min, bw_300);
356          assert_eq!(f.estimate[1].value, bw_400);
357          assert_eq!(f.estimate[2].value, bw_400);
358  
359          time += Duration::from_millis(300);
360          bw_min = f.running_min(win, time, bw_500);
361          assert_eq!(bw_min, bw_300);
362          assert_eq!(f.estimate[1].value, bw_400);
363          assert_eq!(f.estimate[2].value, bw_500);
364  
365          time += Duration::from_millis(300);
366          bw_min = f.running_min(win, time, bw_200);
367          assert_eq!(bw_min, bw_200);
368          assert_eq!(f.estimate[1].value, bw_200);
369          assert_eq!(f.estimate[2].value, bw_200);
370      }
371  
372      #[test]
get_windowed_max_estimates_rtt()373      fn get_windowed_max_estimates_rtt() {
374          let mut f = Minmax::new(Duration::new(0, 0));
375          let rtt_25 = Duration::from_millis(25);
376          let rtt_24 = Duration::from_millis(24);
377          let rtt_23 = Duration::from_millis(23);
378          let rtt_26 = Duration::from_millis(26);
379          let win = Duration::from_secs(1);
380          let mut time = Instant::now();
381  
382          let mut rtt_max = f.reset(time, rtt_25);
383          assert_eq!(rtt_max, rtt_25);
384  
385          time += Duration::from_millis(300);
386          rtt_max = f._running_max(win, time, rtt_24);
387          assert_eq!(rtt_max, rtt_25);
388          assert_eq!(f.estimate[1].value, rtt_24);
389          assert_eq!(f.estimate[2].value, rtt_24);
390  
391          time += Duration::from_millis(300);
392          rtt_max = f._running_max(win, time, rtt_23);
393          assert_eq!(rtt_max, rtt_25);
394          assert_eq!(f.estimate[1].value, rtt_24);
395          assert_eq!(f.estimate[2].value, rtt_23);
396  
397          time += Duration::from_millis(300);
398          rtt_max = f._running_max(win, time, rtt_26);
399          assert_eq!(rtt_max, rtt_26);
400          assert_eq!(f.estimate[1].value, rtt_26);
401          assert_eq!(f.estimate[2].value, rtt_26);
402      }
403  
404      #[test]
get_windowed_max_estimates_bandwidth()405      fn get_windowed_max_estimates_bandwidth() {
406          let mut f = Minmax::new(0);
407          let bw_500 = 500;
408          let bw_400 = 400;
409          let bw_300 = 300;
410          let bw_600 = 600;
411          let win = Duration::from_secs(1);
412          let mut time = Instant::now();
413  
414          let mut bw_max = f.reset(time, bw_500);
415          assert_eq!(bw_max, bw_500);
416  
417          time += Duration::from_millis(300);
418          bw_max = f._running_max(win, time, bw_400);
419          assert_eq!(bw_max, bw_500);
420          assert_eq!(f.estimate[1].value, bw_400);
421          assert_eq!(f.estimate[2].value, bw_400);
422  
423          time += Duration::from_millis(300);
424          bw_max = f._running_max(win, time, bw_300);
425          assert_eq!(bw_max, bw_500);
426          assert_eq!(f.estimate[1].value, bw_400);
427          assert_eq!(f.estimate[2].value, bw_300);
428  
429          time += Duration::from_millis(300);
430          bw_max = f._running_max(win, time, bw_600);
431          assert_eq!(bw_max, bw_600);
432          assert_eq!(f.estimate[1].value, bw_600);
433          assert_eq!(f.estimate[2].value, bw_600);
434      }
435  }
436