diff --git a/PerfAndStressTest/PerfAndStressTest.cpp b/PerfAndStressTest/PerfAndStressTest.cpp index 78b9e8207..3f391b70c 100644 --- a/PerfAndStressTest/PerfAndStressTest.cpp +++ b/PerfAndStressTest/PerfAndStressTest.cpp @@ -160,6 +160,27 @@ namespace PerfAndStressTest Assert::AreEqual(ret, 0); } + TEST_METHOD(bdp_short) + { + int ret = bdp_short_test(); + + Assert::AreEqual(ret, 0); + } + + TEST_METHOD(bdp_short_hi) + { + int ret = bdp_short_hi_test(); + + Assert::AreEqual(ret, 0); + } + + TEST_METHOD(bdp_short_lo) + { + int ret = bdp_short_lo_test(); + + Assert::AreEqual(ret, 0); + } + TEST_METHOD(stress) { int ret = stress_test(); diff --git a/picoquic/bbr.c b/picoquic/bbr.c index 833ee90aa..0b14d978b 100644 --- a/picoquic/bbr.c +++ b/picoquic/bbr.c @@ -77,7 +77,8 @@ typedef enum { picoquic_bbr_alg_probe_bw_refill, picoquic_bbr_alg_probe_bw_up, picoquic_bbr_alg_probe_rtt, - picoquic_bbr_alg_startup_long_rtt + picoquic_bbr_alg_startup_long_rtt, + picoquic_bbr_alg_startup_resume } picoquic_bbr_alg_state_t; typedef enum { @@ -89,8 +90,9 @@ typedef enum { /* Constants in BBRv3 */ #define BBRPacingMarginPercent 1 /* discount factor of 1% used to scale BBR.bw to produce BBR.pacing_rate */ -#define BBRStartupPacingGain 2.77 /* constant, 4*ln(2), approx 2.77 */ -#define BBRStartupCwndGain 2.0 /* constant */ + + + #define BBRLossThresh 0.2 /* maximum tolerated packet loss (default: 20%) */ #define BBRBeta 0.7 /* Multiplicative decrease on packet loss (default: 0.7) */ @@ -105,6 +107,14 @@ typedef enum { #define BBRProbeRTTDuration 200000 /* 200msec, 200000 microsecs */ #define BBRProbeRTTInterval 5000000 /* 5 seconds */ +#define BBRStartupPacingGain 2.77 /* constant, 4*ln(2), approx 2.77 */ +#define BBRStartupCwndGain 2.0 /* constant */ +#define BBRStartupIncreaseThreshold 1.25 + +#define BBRStartupResumePacingGain 1.25 /* arbitrary */ +#define BBRStartupResumeCwndGain 1.25 /* arbitrary */ +#define BBRStartupResumeIncreaseThreshold 1.125 + #define BBRProbeBwDownPacingGain 0.9 #define BBRProbeBwDownCwndGain 2.0 #define BBRProbeBwCruisePacingGain 1.0 @@ -113,6 +123,7 @@ typedef enum { #define BBRProbeBwRefillCwndGain 2.0 #define BBRProbeBwUpPacingGain 1.25 #define BBRProbeBwUpCwndGain 2.25 + #define BBRMinRttMarginPercent 5 /* Margin factor of 20% for avoiding firing RTT Probe too often */ #define BBRLongRttThreshold 250000 @@ -206,6 +217,7 @@ typedef struct st_picoquic_bbr_state_t { /* manage startup long_rtt */ picoquic_min_max_rtt_t rtt_filter; uint64_t bdp_seed; + unsigned int probe_bdp_seed; /* Experimental extensions, may or maynot be a good idea. */ uint64_t wifi_shadow_rtt; /* Shadow RTT used for wifi connections. */ @@ -265,6 +277,7 @@ static void BBRStartProbeBW_CRUISE(picoquic_bbr_state_t* bbr_state); static void BBRStartProbeBW_REFILL(picoquic_bbr_state_t* bbr_state, picoquic_path_t * path_x); static void BBREnterStartup(picoquic_bbr_state_t* bbr_state); static void BBRReEnterStartup(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time); +static void BBRCheckStartupHighLoss(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, bbr_per_ack_state_t* rs); static void BBRUpdateRound(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x); static void BBRStartRound(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x); static void BBRSetRsFromAckState(picoquic_path_t* path_x, picoquic_per_ack_state_t* ack_state, bbr_per_ack_state_t* rs); @@ -496,6 +509,10 @@ static void BBRSetCwnd(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, path_x->cwin = bbr_state->max_inflight; } } + else if (bbr_state->state == picoquic_bbr_alg_startup_resume && + bbr_state->bdp_seed > path_x->cwin) { + path_x->cwin = bbr_state->bdp_seed; + } else if (path_x->cwin < bbr_state->max_inflight || path_x->delivered < PICOQUIC_CWIN_INITIAL) { path_x->cwin = path_x->cwin+ rs->newly_acked; } @@ -678,12 +695,19 @@ static void BBRUpdateMaxInflight(picoquic_bbr_state_t* bbr_state, picoquic_path_ * BBRUpdateModelAndState(). There is probably no need to do an extra * call here. */ uint64_t inflight = BBRBDPMultiple(bbr_state, path_x, bbr_state->cwnd_gain); + inflight += bbr_state->extra_acked; if (bbr_state->min_rtt < bbr_state->wifi_shadow_rtt && bbr_state->min_rtt > 0){ inflight = (uint64_t)(((double)inflight) * ((double)bbr_state->wifi_shadow_rtt) / ((double)bbr_state->min_rtt)); } bbr_state->max_inflight = BBRQuantizationBudget(bbr_state, path_x, inflight); +#if 0 + if (bbr_state->state == picoquic_bbr_alg_startup && + bbr_state->bdp_seed > bbr_state->max_inflight) { + bbr_state->max_inflight = bbr_state->bdp_seed; + } +#endif } /* Pacing rate functions */ @@ -701,6 +725,16 @@ static void BBRInitPacingRate(picoquic_bbr_state_t* bbr_state, picoquic_path_t* static void BBRSetPacingRateWithGain(picoquic_bbr_state_t* bbr_state, double pacing_gain) { double rate = pacing_gain * ((double)(bbr_state->bw * (100 - BBRPacingMarginPercent))) / (double)100; + + if (bbr_state->state == picoquic_bbr_alg_startup_resume && + !bbr_state->filled_pipe && + bbr_state->bdp_seed > 0) { + double bdp_rate = (((double)bbr_state->bdp_seed*1000000.0) / (double)bbr_state->min_rtt); + if (bdp_rate > rate) { + rate = bdp_rate; + } + } + if (bbr_state->filled_pipe || rate > bbr_state->pacing_rate) { bbr_state->pacing_rate = rate; } @@ -1459,6 +1493,57 @@ static void BBRCheckDrain(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path } /* End of drain specific algorithms */ +/* Startup extension to support careful resume */ +static void BBRCheckStartupFullBandwidthGeneric(picoquic_bbr_state_t* bbr_state, + bbr_per_ack_state_t * rs, double threshold) +{ + if (bbr_state->filled_pipe || + !bbr_state->round_start || rs->is_app_limited) { + return; /* no need to check for a full pipe now */ + } + + if ((double)bbr_state->max_bw >= threshold*((double)bbr_state->full_bw)) { + /* still growing? */ + bbr_state->full_bw = bbr_state->max_bw; /* record new baseline level */ + bbr_state->full_bw_count = 0; + return; + } + bbr_state->full_bw_count++; /* another round w/o much growth */ + if (bbr_state->full_bw_count >= 3) { + bbr_state->filled_pipe = 1; + } +} + +static void BBREnterStartupResume(picoquic_bbr_state_t* bbr_state) +{ + /* This code is called either when the "bdp seed" is set, or + * upon "Enter Startup" + */ + bbr_state->state = picoquic_bbr_alg_startup_resume; + bbr_state->pacing_gain = BBRStartupResumePacingGain; + bbr_state->cwnd_gain = BBRStartupResumeCwndGain; +} + +static void BBRCheckStartupResume(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, bbr_per_ack_state_t* rs, uint64_t current_time) +{ + if (bbr_state->state == picoquic_bbr_alg_startup_resume) { + BBRCheckStartupHighLoss(bbr_state, path_x, rs); + if (!bbr_state->filled_pipe && (double)bbr_state->max_bw > BBRStartupResumeIncreaseThreshold * bbr_state->bdp_seed) { + BBREnterStartup(bbr_state); + } + else { + BBRCheckStartupFullBandwidthGeneric(bbr_state, rs, BBRStartupResumeIncreaseThreshold); + if (bbr_state->filled_pipe) { + if (bbr_state->full_bw_count > 0) { + bbr_state->probe_probe_bw_quickly = 1; + bbr_state->full_bw_count = 0; + } + BBREnterDrain(bbr_state, path_x, current_time); + } + } + } +} + /* Startup specific processes for BBRv3 */ static void BBRCheckStartupHighLoss(picoquic_bbr_state_t* bbr_state, picoquic_path_t * path_x, bbr_per_ack_state_t * rs) { @@ -1500,16 +1585,18 @@ static void BBRCheckStartupFullBandwidth(picoquic_bbr_state_t* bbr_state, static void BBRCheckStartupDone(picoquic_bbr_state_t* bbr_state, picoquic_path_t * path_x, bbr_per_ack_state_t * rs, uint64_t current_time) { - BBRCheckStartupFullBandwidth(bbr_state, rs); - BBRCheckStartupHighLoss(bbr_state, path_x, rs); + if (bbr_state->state == picoquic_bbr_alg_startup) { + BBRCheckStartupFullBandwidth(bbr_state, rs); + BBRCheckStartupHighLoss(bbr_state, path_x, rs); - if (bbr_state->state == picoquic_bbr_alg_startup && - bbr_state->filled_pipe) { - if (bbr_state->full_bw_count > 0) { - bbr_state->probe_probe_bw_quickly = 1; - bbr_state->full_bw_count = 0; + if (bbr_state->state == picoquic_bbr_alg_startup && + bbr_state->filled_pipe) { + if (bbr_state->full_bw_count > 0) { + bbr_state->probe_probe_bw_quickly = 1; + bbr_state->full_bw_count = 0; + } + BBREnterDrain(bbr_state, path_x, current_time); } - BBREnterDrain(bbr_state, path_x, current_time); } } @@ -1580,7 +1667,8 @@ static void BBRExitStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path void BBRCheckStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, bbr_per_ack_state_t* rs, uint64_t current_time) { - if (bbr_state->state == picoquic_bbr_alg_startup && + if ((bbr_state->state == picoquic_bbr_alg_startup || + bbr_state->state == picoquic_bbr_alg_startup_resume) && path_x->rtt_min > BBRLongRttThreshold) { BBREnterStartupLongRTT(bbr_state, path_x); } @@ -1623,6 +1711,10 @@ void BBRUpdateStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* p void BBRSetBdpSeed(picoquic_bbr_state_t* bbr_state, uint64_t bdp_seed) { bbr_state->bdp_seed = bdp_seed; + if (bbr_state->state == picoquic_bbr_alg_startup && + bbr_state->bdp_seed > bbr_state->max_bw) { + BBREnterStartupResume(bbr_state); + } } /* BBRv3 per loss steps. @@ -1696,6 +1788,7 @@ static void BBRUpdateModelAndState(picoquic_bbr_state_t* bbr_state, picoquic_pat BBRUpdateCongestionSignals(bbr_state, path_x, rs); BBRUpdateACKAggregation(bbr_state, path_x, rs, current_time); BBRCheckStartupLongRtt(bbr_state, path_x, rs, current_time); + BBRCheckStartupResume(bbr_state, path_x, rs, current_time); BBRCheckStartupDone(bbr_state, path_x, rs, current_time); BBRCheckRecovery(bbr_state, path_x, rs, current_time); BBRCheckDrain(bbr_state, path_x, current_time); diff --git a/picoquic_t/picoquic_t.c b/picoquic_t/picoquic_t.c index 591afec5e..66801a90f 100644 --- a/picoquic_t/picoquic_t.c +++ b/picoquic_t/picoquic_t.c @@ -346,6 +346,9 @@ static const picoquic_test_def_t test_table[] = { #if 0 { "bdp_cubic", bdp_cubic_test }, #endif + { "bdp_short", bdp_short_test }, + { "bdp_short_hi", bdp_short_hi_test }, + { "bdp_short_lo", bdp_short_lo_test }, { "cid_length", cid_length_test }, { "optimistic_ack", optimistic_ack_test }, { "optimistic_hole", optimistic_hole_test }, diff --git a/picoquictest/picoquictest.h b/picoquictest/picoquictest.h index ebea77977..6e10eeeac 100644 --- a/picoquictest/picoquictest.h +++ b/picoquictest/picoquictest.h @@ -267,6 +267,9 @@ int bdp_cubic_test(); int bdp_rtt_test(); int bdp_ip_test(); int bdp_delay_test(); +int bdp_short_test(); +int bdp_short_hi_test(); +int bdp_short_lo_test(); int long_rtt_test(); int high_latency_basic_test(); int high_latency_bbr_test(); diff --git a/picoquictest/tls_api_test.c b/picoquictest/tls_api_test.c index 3f99ac889..5b8701583 100644 --- a/picoquictest/tls_api_test.c +++ b/picoquictest/tls_api_test.c @@ -12505,7 +12505,10 @@ typedef enum { bdp_test_option_ip, bdp_test_option_delay, bdp_test_option_reno, - bdp_test_option_cubic + bdp_test_option_cubic, + bdp_test_option_short, + bdp_test_option_short_lo, + bdp_test_option_short_hi, } bdp_test_option_enum; int bdp_option_test_one(bdp_test_option_enum bdp_test_option) @@ -12545,7 +12548,29 @@ int bdp_option_test_one(bdp_test_option_enum bdp_test_option) test_ctx->c_to_s_link->picosec_per_byte = (1000000ull * 8) / 20; test_ctx->s_to_c_link->picosec_per_byte = (1000000ull * 8) / 20; - if (i > 0) { + if (bdp_test_option == bdp_test_option_short || + bdp_test_option == bdp_test_option_short_lo || + bdp_test_option == bdp_test_option_short_hi) { + /* Test that the BDP option also works well if delay < 250 ms */ + max_completion_time = 4500000; + test_ctx->c_to_s_link->microsec_latency = 100000ull; + test_ctx->s_to_c_link->microsec_latency = 100000ull; + buffer_size = 2 * test_ctx->c_to_s_link->microsec_latency; + if (i == 0) { + if (bdp_test_option == bdp_test_option_short_lo) { + test_ctx->c_to_s_link->picosec_per_byte *= 2; + test_ctx->s_to_c_link->picosec_per_byte *= 2; + } + else if (bdp_test_option == bdp_test_option_short_hi) { + test_ctx->c_to_s_link->picosec_per_byte /= 2; + test_ctx->s_to_c_link->picosec_per_byte /= 2; + } + } + else if (i == 1 && bdp_test_option == bdp_test_option_short_lo) { + max_completion_time = 4650000; + } + } + else if (i > 0) { switch (bdp_test_option) { case bdp_test_option_none: break; @@ -12637,6 +12662,9 @@ int bdp_option_test_one(bdp_test_option_enum bdp_test_option) } else if (bdp_test_option == bdp_test_option_basic || bdp_test_option == bdp_test_option_reno || + bdp_test_option == bdp_test_option_short || + bdp_test_option == bdp_test_option_short_hi || + bdp_test_option == bdp_test_option_short_lo || bdp_test_option == bdp_test_option_cubic) { if (!test_ctx->cnx_server->cwin_notified_from_seed) { DBG_PRINTF("BDP RTT test (bdp test: %d), cnx %d, cwin not seed on server.\n", @@ -12712,6 +12740,21 @@ int bdp_reno_test() return bdp_option_test_one(bdp_test_option_reno); } +int bdp_short_test() +{ + return bdp_option_test_one(bdp_test_option_short); +} + +int bdp_short_hi_test() +{ + return bdp_option_test_one(bdp_test_option_short_hi); +} + +int bdp_short_lo_test() +{ + return bdp_option_test_one(bdp_test_option_short_lo); +} + #if defined(_WINDOWS) && !defined(_WINDOWS64) int bdp_cubic_test() {