test_minmax.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
2/* Tests adopted from Chromium windowed_filter_test.cc */
3// Copyright (c) 2016 The Chromium Authors. All rights reserved.
4
5#include <assert.h>
6#include <stdint.h>
7#include <string.h>
8
9#include "lsquic_minmax.h"
10
11/* Convert milliseconds to lsquic_time_t, which is microseconds */
12#define ms(val) ((val) * 1000)
13
14static void
15init_minmax (struct minmax *minmax)
16{
17    minmax_init(minmax, ms(99));
18}
19
20
21// Sets up windowed_min_rtt_ to have the following values:
22// Best = 20ms, recorded at 25ms
23// Second best = 30ms, recorded at 50ms
24// Third best = 50ms, recorded at 100ms
25static void
26init_min_filter (struct minmax *minmax)
27{
28    uint64_t now, rtt;
29    unsigned i;
30
31    now = 0;
32    rtt = ms(10);
33    for (i = 0; i < 5; ++i)
34    {
35        minmax_upmin(minmax, now, rtt);
36        now += ms(25);
37        rtt += ms(10);
38    }
39    assert(ms(20) == minmax_get_idx(minmax, 0));
40    assert(ms(30) == minmax_get_idx(minmax, 1));
41    assert(ms(50) == minmax_get_idx(minmax, 2));
42}
43
44
45// Sets up windowed_max_bw_ to have the following values:
46// Best = 900 bps, recorded at 25ms
47// Second best = 800 bps, recorded at 50ms
48// Third best = 600 bps, recorded at 100ms
49static void
50init_max_filter (struct minmax *minmax)
51{
52    uint64_t now, bw;
53    unsigned i;
54
55    now = 0;
56    bw = 1000;
57    for (i = 0; i < 5; ++i)
58    {
59        minmax_upmax(minmax, now, bw);
60        now += ms(25);
61        bw -= 100;
62    }
63    assert(900 == minmax_get_idx(minmax, 0));
64    assert(800 == minmax_get_idx(minmax, 1));
65    assert(600 == minmax_get_idx(minmax, 2));
66}
67
68
69// Test helper function: updates the filter with a lot of small values in order
70// to ensure that it is not susceptible to noise.
71static void
72update_with_irrelevant_samples (struct minmax *minmax, uint64_t max_value,
73                                                                uint64_t time)
74{
75    uint64_t i;
76
77    for (i = 0; i < 1000; ++i)
78        minmax_upmax(minmax, time, i % max_value);
79}
80
81
82static void
83test_uninitialized_estimates (void)
84{
85    struct minmax minmax;
86
87    init_minmax(&minmax);
88    assert(0 == minmax_get_idx(&minmax, 0));
89    assert(0 == minmax_get_idx(&minmax, 1));
90    assert(0 == minmax_get_idx(&minmax, 2));
91}
92
93
94static void
95test_monotonically_increasing_min (void)
96{
97    struct minmax minmax;
98    uint64_t rtt, now;
99    unsigned i;
100
101    rtt = ms(10);
102    now = 0;
103
104    init_minmax(&minmax);
105    minmax_upmin(&minmax, now, rtt);
106
107    assert(ms(10) == minmax_get(&minmax));
108    // Gradually increase the rtt samples and ensure the windowed min rtt starts
109    // rising.
110    for (i = 0; i < 6; ++i)
111    {
112        now += ms(25);
113        rtt += ms(10);
114        minmax_upmin(&minmax, now, rtt);
115        if (i < 3)
116            assert(minmax_get(&minmax) == ms(10));
117        else if (i == 3)
118            assert(minmax_get(&minmax) == ms(20));
119        else if (i == 4)
120            assert(minmax_get(&minmax) == ms(30));
121        else
122            assert(minmax_get(&minmax) == ms(50));
123    }
124}
125
126
127static void
128test_monotonically_decreasing_max (void)
129{
130    struct minmax minmax;
131    uint64_t bw, now;
132    unsigned i;
133
134    bw = 1000;
135    now = 0;
136
137    init_minmax(&minmax);
138    minmax_upmax(&minmax, now, bw);
139
140    assert(1000 == minmax_get(&minmax));
141    // Gradually decrease the bw samples and ensure the windowed max bw starts
142    // decreasing.
143    for (i = 0; i < 6; ++i)
144    {
145        now += ms(25);
146        bw -= 100;
147        minmax_upmax(&minmax, now, bw);
148        if (i < 3)
149            assert(minmax_get(&minmax) == 1000);
150        else if (i == 3)
151            assert(minmax_get(&minmax) == 900);
152        else if (i == 4)
153            assert(minmax_get(&minmax) == 800);
154        else
155            assert(minmax_get(&minmax) == 600);
156    }
157}
158
159
160static void
161sample_changes_third_best_min (void)
162{
163    struct minmax minmax;
164    uint64_t rtt, now;
165
166    init_minmax(&minmax);
167    init_min_filter(&minmax);
168    rtt = minmax_get_idx(&minmax, 2);
169    rtt -= ms(5);
170    now = ms(101);
171    minmax_upmin(&minmax, now, rtt);
172    assert(rtt == minmax_get_idx(&minmax, 2));
173    assert(ms(30) == minmax_get_idx(&minmax, 1));
174    assert(ms(20) == minmax_get_idx(&minmax, 0));
175}
176
177
178static void
179sample_changes_third_best_max (void)
180{
181    struct minmax minmax;
182    uint64_t bw, now;
183
184    init_minmax(&minmax);
185    init_max_filter(&minmax);
186    bw = minmax_get_idx(&minmax, 2);
187    bw += 50;
188    now = ms(101);
189    minmax_upmax(&minmax, now, bw);
190    assert(bw == minmax_get_idx(&minmax, 2));
191    assert(800 == minmax_get_idx(&minmax, 1));
192    assert(900 == minmax_get_idx(&minmax, 0));
193}
194
195
196// RTT sample lower than the second-choice min sets that and also
197// the third-choice min.
198static void
199sample_changes_second_best_min (void)
200{
201    struct minmax minmax;
202    uint64_t rtt, now;
203
204    init_minmax(&minmax);
205    init_min_filter(&minmax);
206    rtt = minmax_get_idx(&minmax, 1);
207    rtt -= ms(5);
208    now = ms(101);
209    minmax_upmin(&minmax, now, rtt);
210    assert(rtt == minmax_get_idx(&minmax, 2));
211    assert(rtt == minmax_get_idx(&minmax, 1));
212    assert(ms(20) == minmax_get_idx(&minmax, 0));
213}
214
215
216// BW sample higher than the second-choice max sets that and also
217// the third-choice max.
218static void
219sample_changes_second_best_max (void)
220{
221    struct minmax minmax;
222    uint64_t bw, now;
223
224    init_minmax(&minmax);
225    init_max_filter(&minmax);
226    bw = minmax_get_idx(&minmax, 1);
227    bw += 50;
228    now = ms(101);
229    minmax_upmax(&minmax, now, bw);
230    assert(bw == minmax_get_idx(&minmax, 2));
231    assert(bw == minmax_get_idx(&minmax, 1));
232    assert(900 == minmax_get_idx(&minmax, 0));
233}
234
235
236// RTT sample lower than the first-choice min-rtt sets that and also
237// the second and third-choice mins.
238static void
239sample_changes_all_mins (void)
240{
241    struct minmax minmax;
242    uint64_t rtt, now;
243
244    init_minmax(&minmax);
245    init_min_filter(&minmax);
246    rtt = minmax_get(&minmax);
247    rtt -= ms(5);
248    now = ms(101);
249    minmax_upmin(&minmax, now, rtt);
250    assert(rtt == minmax_get_idx(&minmax, 2));
251    assert(rtt == minmax_get_idx(&minmax, 1));
252    assert(rtt == minmax_get_idx(&minmax, 0));
253}
254
255
256// BW sample higher than the first-choice max sets that and also
257// the second and third-choice maxs.
258static void
259sample_changes_all_maxs (void)
260{
261    struct minmax minmax;
262    uint64_t bw, now;
263
264    init_minmax(&minmax);
265    init_max_filter(&minmax);
266    bw = minmax_get(&minmax);
267    bw += 50;
268    now = ms(101);
269    minmax_upmax(&minmax, now, bw);
270    assert(bw == minmax_get_idx(&minmax, 2));
271    assert(bw == minmax_get_idx(&minmax, 1));
272    assert(bw == minmax_get_idx(&minmax, 0));
273}
274
275
276// Best min sample was recorded at 25ms, so expiry time is 124ms.
277static void
278expire_best_min (void)
279{
280    struct minmax minmax;
281    uint64_t rtt, now, old_2nd, old_3rd;
282
283    init_minmax(&minmax);
284    init_min_filter(&minmax);
285    old_3rd = minmax_get_idx(&minmax, 2);
286    old_2nd = minmax_get_idx(&minmax, 1);
287    rtt = old_3rd + ms(5);
288    now = ms(125);
289    minmax_upmin(&minmax, now, rtt);
290    assert(rtt == minmax_get_idx(&minmax, 2));
291    assert(old_3rd == minmax_get_idx(&minmax, 1));
292    assert(old_2nd == minmax_get(&minmax));
293}
294
295
296// Best max sample was recorded at 25ms, so expiry time is 124ms.
297static void
298expire_best_max (void)
299{
300    struct minmax minmax;
301    uint64_t bw, now, old_2nd, old_3rd;
302
303    init_minmax(&minmax);
304    init_max_filter(&minmax);
305    old_3rd = minmax_get_idx(&minmax, 2);
306    old_2nd = minmax_get_idx(&minmax, 1);
307    bw = old_3rd - 50;
308    now = ms(125);
309    minmax_upmax(&minmax, now, bw);
310    assert(bw == minmax_get_idx(&minmax, 2));
311    assert(old_3rd == minmax_get_idx(&minmax, 1));
312    assert(old_2nd == minmax_get(&minmax));
313}
314
315
316// Second best min sample was recorded at 75ms, so expiry time is 174ms.
317static void
318expire_second_best_min (void)
319{
320    struct minmax minmax;
321    uint64_t rtt, now, old_3rd;
322
323    init_minmax(&minmax);
324    init_min_filter(&minmax);
325    old_3rd = minmax_get_idx(&minmax, 2);
326    rtt = old_3rd + ms(5);
327    now = ms(175);
328    minmax_upmin(&minmax, now, rtt);
329    assert(rtt == minmax_get_idx(&minmax, 2));
330    assert(rtt == minmax_get_idx(&minmax, 1));
331    assert(old_3rd == minmax_get(&minmax));
332}
333
334
335// Second best max sample was recorded at 75ms, so expiry time is 174ms.
336static void
337expire_second_best_max (void)
338{
339    struct minmax minmax;
340    uint64_t bw, now, old_3rd;
341
342    init_minmax(&minmax);
343    init_max_filter(&minmax);
344    old_3rd = minmax_get_idx(&minmax, 2);
345    bw = old_3rd - 50;
346    now = ms(175);
347    minmax_upmax(&minmax, now, bw);
348    assert(bw == minmax_get_idx(&minmax, 2));
349    assert(bw == minmax_get_idx(&minmax, 1));
350    assert(old_3rd == minmax_get(&minmax));
351}
352
353
354// Third best min sample was recorded at 100ms, so expiry time is 199ms.
355static void
356expire_all_mins (void)
357{
358    struct minmax minmax;
359    uint64_t rtt, now, old_3rd;
360
361    init_minmax(&minmax);
362    init_min_filter(&minmax);
363    old_3rd = minmax_get_idx(&minmax, 2);
364    rtt = old_3rd + ms(5);
365    now = ms(200);
366    minmax_upmin(&minmax, now, rtt);
367    assert(rtt == minmax_get_idx(&minmax, 2));
368    assert(rtt == minmax_get_idx(&minmax, 1));
369    assert(rtt == minmax_get(&minmax));
370}
371
372
373// Third best max sample was recorded at 100ms, so expiry time is 199ms.
374static void
375expire_all_maxs (void)
376{
377    struct minmax minmax;
378    uint64_t bw, now, old_3rd;
379
380    init_minmax(&minmax);
381    init_max_filter(&minmax);
382    old_3rd = minmax_get_idx(&minmax, 2);
383    bw = old_3rd - 50;
384    now = ms(200);
385    minmax_upmax(&minmax, now, bw);
386    assert(bw == minmax_get_idx(&minmax, 2));
387    assert(bw == minmax_get_idx(&minmax, 1));
388    assert(bw == minmax_get(&minmax));
389}
390
391
392// Test the windowed filter where the time used is an exact counter instead of a
393// timestamp.  This is useful if, for example, the time is measured in round
394// trips.
395static void
396expire_counter_based_max (void)
397{
398    struct minmax minmax;
399
400    // Create a window which starts at t = 0 and expires after two cycles.
401    minmax_init(&minmax, 2);
402
403    const uint64_t kBest = 50000;
404    // Insert 50000 at t = 1.
405    minmax_upmax(&minmax, 1, 50000);
406    assert(kBest == minmax_get(&minmax));
407    update_with_irrelevant_samples(&minmax, 20, 1);
408    assert(kBest == minmax_get(&minmax));
409
410    // Insert 40000 at t = 2.  Nothing is expected to expire.
411    minmax_upmax(&minmax, 2, 40000);
412    assert(kBest == minmax_get(&minmax));
413    update_with_irrelevant_samples(&minmax, 20, 2);
414    assert(kBest == minmax_get(&minmax));
415
416    // Insert 30000 at t = 3.  Nothing is expected to expire yet.
417    minmax_upmax(&minmax, 3, 30000);
418    assert(kBest == minmax_get(&minmax));
419    update_with_irrelevant_samples(&minmax, 20, 3);
420    assert(kBest == minmax_get(&minmax));
421
422    // Insert 20000 at t = 4.  50000 at t = 1 expires, so 40000 becomes the new
423    // maximum.
424    const uint64_t kNewBest = 40000;
425    minmax_upmax(&minmax, 4, 20000);
426    assert(kNewBest == minmax_get(&minmax));
427    update_with_irrelevant_samples(&minmax, 20, 4);
428    assert(kNewBest == minmax_get(&minmax));
429}
430
431
432int
433main (void)
434{
435    test_uninitialized_estimates();
436    test_monotonically_increasing_min();
437    test_monotonically_decreasing_max();
438    sample_changes_third_best_min();
439    sample_changes_third_best_max();
440    sample_changes_second_best_min();
441    sample_changes_second_best_max();
442    sample_changes_all_mins();
443    sample_changes_all_maxs();
444    expire_best_min();
445    expire_best_max();
446    expire_second_best_min();
447    expire_second_best_max();
448    expire_all_mins();
449    expire_all_maxs();
450    expire_counter_based_max();
451
452    return 0;
453}
454