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