Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e9d45a3
HyStart++ impl, HyStart++ (D)CUBIC example, HyStart as option
hfstco Feb 17, 2025
aa087f9
moved functions to CC part of the code
hfstco Feb 18, 2025
186816b
Merge branch 'private-octopus:master' into hystart++_as_option
hfstco Feb 18, 2025
d16ddfa
added simple example hystart++ test case
hfstco Feb 18, 2025
27e2c28
try to fix linker error
hfstco Feb 18, 2025
c4f5436
Add hystart_test.c to VS solution
huitema Feb 18, 2025
c4b48c9
Merge remote-tracking branch 'refs/remotes/private_octopus/master' in…
hfstco Feb 19, 2025
95b3512
HyStart++ picoquic_ns impl & example
hfstco Feb 19, 2025
a91228b
introduced hystart_alg enum, typo, hystart = 0 (default)
hfstco Feb 19, 2025
77569c6
unittest1 fix
hfstco Feb 19, 2025
1a07304
prague impl
hfstco Feb 20, 2025
549c2f3
partial hystart++ support for newreno
hfstco Feb 25, 2025
736354e
newreno hystart++ bypass to avoid multiple failing tests, until newre…
hfstco Feb 25, 2025
4e0ce94
Merge remote-tracking branch 'private_octopus/master' into hystart++
hfstco Mar 5, 2025
c02d06f
Merge remote-tracking branch 'refs/remotes/private_octopus/master' in…
hfstco Mar 17, 2025
a0bc6b5
IS_IN_CSS fix, window_end fix
hfstco Mar 17, 2025
2f27936
Merge remote-tracking branch 'private_octopus/master' into hystart++
hfstco Apr 16, 2025
5952858
Merge remote-tracking branch 'private_octopus/master' into hystart++
hfstco Apr 20, 2025
9887a91
Merge remote-tracking branch 'private_octopus/master' into hystart++
hfstco May 1, 2025
8ba66ec
Remove HyStart algorithm configuration for enhanced modularity
hfstco May 3, 2025
5fe159a
Add cc_compete_cubic2_hystart_pp_test function.
hfstco May 4, 2025
9477fd2
Update config usage reference to remove HyStart option
hfstco May 8, 2025
cd68dd1
Refactor HyStart++ logic into a shared utility function.
hfstco May 8, 2025
3ede626
Refactor HyStart++ initialization by removing redundancy
hfstco May 8, 2025
b09e564
Remove deprecated HYSTART option from configuration enum
hfstco May 8, 2025
b5fc0d9
Add HyStart++ support to BBR congestion control.
hfstco May 8, 2025
b93ba29
Consolidate hystart tests into a single unified framework
hfstco May 9, 2025
b107c2e
BBR fix and HyStart tests
hfstco May 9, 2025
9ec0ad9
Revert CMakeLists.txt compiler optimization changes
hfstco May 9, 2025
1b19377
Merge remote-tracking branch 'picoquic/master' into hystart++
hfstco Aug 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ set(PICOQUIC_TEST_LIBRARY_FILES
picoquictest/getter_test.c
picoquictest/hashtest.c
picoquictest/high_latency_test.c
picoquictest/hystart_test.c
picoquictest/intformattest.c
picoquictest/l4s_test.c
picoquictest/mbedtls_test.c
Expand Down
25 changes: 25 additions & 0 deletions UnitTest1/unittest1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,13 @@ namespace UnitTest1
Assert::AreEqual(ret, 0);
}

TEST_METHOD(cc_compete_cubic2_hystart_pp)
{
int ret = cc_compete_cubic2_hystart_pp_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(cc_compete_prague2)
{
int ret = cc_compete_prague2_test();
Expand Down Expand Up @@ -3281,6 +3288,24 @@ namespace UnitTest1
Assert::AreEqual(ret, 0);
}

TEST_METHOD(slow_start_example) {
int ret = slow_start_example_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(hystart_example) {
int ret = hystart_example_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(hystart_pp_example_test) {
int ret = slow_start_example_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(cplusplus) {
int ret = cplusplustest();

Expand Down
1 change: 1 addition & 0 deletions picohttp_t/picohttp_t.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ static const picoquic_test_def_t test_table[] = {
{ "quicperf_multi", quicperf_multi_test },
{ "quicperf_overflow", quicperf_overflow_test },
{ "cc_compete_cubic2", cc_compete_cubic2_test },
{ "cc_compete_cubic2_hystart_pp", cc_compete_cubic2_hystart_pp_test },
{ "cc_compete_prague2", cc_compete_prague2_test },
{ "cc_compete_d_cubic", cc_compete_d_cubic_test },
{ "cc_ns_asym", cc_ns_asym_test },
Expand Down
110 changes: 110 additions & 0 deletions picoquic/cc_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,16 @@ uint64_t picoquic_cc_slow_start_increase(picoquic_path_t * path_x, uint64_t nb_d
return nb_delivered;
}

/** For each arriving ACK in slow start, where N is the number of previously unacknowledged bytes acknowledged in
* the arriving ACK:
* Update the cwnd:
* cwnd = cwnd + min(N, L * SMSS)
*/
/** For each arriving ACK in CSS, where N is the number of previously unacknowledged bytes acknowledged in the arriving
* ACK:
* Update the cwnd:
* cwnd = cwnd + (min(N, L * SMSS) / CSS_GROWTH_DIVISOR)
*/
uint64_t picoquic_cc_slow_start_increase_ex(picoquic_path_t * path_x, uint64_t nb_delivered, int in_css)
{
if (in_css) {
Expand Down Expand Up @@ -239,6 +249,106 @@ uint64_t picoquic_cc_slow_start_increase_ex2(picoquic_path_t* path_x, uint64_t n
return picoquic_cc_slow_start_increase_ex(path_x, nb_delivered, in_css);
}

/*
* HyStart++
*/
/** lastRoundMinRTT and currentRoundMinRTT are initialized to infinity at the initialization time. currRTT is
* the RTT sampled from the latest incoming ACK and initialized to infinity.
* - lastRoundMinRTT = infinity
* - currentRoundMinRTT = infinity
* - currRTT = infinity
*/
void picoquic_hystart_pp_reset(picoquic_hystart_pp_state_t* hystart_pp_state) {
/* init round */
hystart_pp_state->current_round.last_round_min_rtt = UINT64_MAX;
hystart_pp_state->current_round.current_round_min_rtt = UINT64_MAX;
//hystart_pp_state.curr_rtt = UINT64_MAX;
hystart_pp_state->current_round.rtt_sample_count = 0;
hystart_pp_state->current_round.window_end = UINT64_MAX;

/* init state */
//hystart_pp_state->rtt_thresh = UINT64_MAX;
hystart_pp_state->css_baseline_min_rtt = UINT64_MAX;
hystart_pp_state->css_round_count = 0;

/* TODO Move start round here. */
}

/** At the start of each round during standard slow start [RFC5681] and CSS, initialize the variables used to
* compute the last round's and current round's minimum RTT:
* - lastRoundMinRTT = currentRoundMinRTT
* - currentRoundMinRTT = infinity
* - rttSampleCount = 0
*/
/** HyStart++ measures rounds using sequence numbers, as follows:
* - Define windowEnd as a sequence number initialized to SND.NXT.
*/
void picoquic_hystart_pp_start_new_round(picoquic_hystart_pp_state_t* hystart_pp_state, picoquic_cnx_t* cnx, picoquic_path_t* path_x) {
hystart_pp_state->current_round.last_round_min_rtt = hystart_pp_state->current_round.current_round_min_rtt;
hystart_pp_state->current_round.current_round_min_rtt = UINT64_MAX;
hystart_pp_state->current_round.rtt_sample_count = 0;

/* Set window end to next sent sequence number. */
hystart_pp_state->current_round.window_end = picoquic_cc_get_sequence_number(cnx, path_x);
}

/** For each arriving ACK in slow start, where N is the number of previously unacknowledged bytes acknowledged in
* the arriving ACK:
* Keep track of the minimum observed RTT:
* currentRoundMinRTT = min(currentRoundMinRTT, currRTT)
* rttSampleCount += 1
*/
/** For each arriving ACK in CSS, where N is the number of previously unacknowledged bytes acknowledged in the arriving
* ACK:
* Keep track of the minimum observed RTT:
* currentRoundMinRTT = min(currentRoundMinRTT, currRTT)
* rttSampleCount += 1
*/
void picoquic_hystart_pp_keep_track(picoquic_hystart_pp_state_t *hystart_pp_state, uint64_t rtt_measurement) {
hystart_pp_state->current_round.current_round_min_rtt = MIN(hystart_pp_state->current_round.current_round_min_rtt, rtt_measurement);
hystart_pp_state->current_round.rtt_sample_count++;
}

/** For rounds where at least N_RTT_SAMPLE RTT samples have been obtained and currentRoundMinRTT and lastRoundMinRTT
* are valid, check to see if delay increase triggers slow start exit:
* if ((rttSampleCount >= N_RTT_SAMPLE) AND (currentRoundMinRTT != infinity) AND (lastRoundMinRTT != infinity))
* RttThresh = max(MIN_RTT_THRESH, min(lastRoundMinRTT / MIN_RTT_DIVISOR, MAX_RTT_THRESH))
* if (currentRoundMinRTT >= (lastRoundMinRTT + RttThresh))
* cssBaselineMinRtt = currentRoundMinRTT
* exit slow start and enter CSS
*/
/** For CSS rounds where at least N_RTT_SAMPLE RTT samples have been obtained, check to see if the current round's
* minRTT drops below baseline (cssBaselineMinRtt) indicating that slow start exit was spurious:
* if (currentRoundMinRTT < cssBaselineMinRtt)
* cssBaselineMinRtt = infinity
* resume slow start including HyStart++
*/
void picoquic_hystart_pp_test(picoquic_hystart_pp_state_t *hystart_pp_state) {
if (hystart_pp_state->css_baseline_min_rtt == UINT64_MAX) {
/* In slow start (SS) */
if (hystart_pp_state->current_round.rtt_sample_count >= PICOQUIC_HYSTART_PP_N_RTT_SAMPLE &&
hystart_pp_state->current_round.current_round_min_rtt != UINT64_MAX &&
hystart_pp_state->current_round.last_round_min_rtt != UINT64_MAX) {
uint64_t rtt_thresh = MAX(PICOQUIC_HYSTART_PP_MIN_RTT_THRESH, MIN(hystart_pp_state->current_round.last_round_min_rtt / PICOQUIC_HYSTART_PP_MIN_RTT_DIVISOR, PICOQUIC_HYSTART_PP_MAX_RTT_THRESH));

if (hystart_pp_state->current_round.current_round_min_rtt >= (hystart_pp_state->current_round.last_round_min_rtt + rtt_thresh)) {
fprintf(stdout, "Enter CSS.\n");
/* Exit slow start and enter CSS. */
hystart_pp_state->css_baseline_min_rtt = hystart_pp_state->current_round.current_round_min_rtt;
}
}
} else {
/* In conservative slow start (CSS) */
if (hystart_pp_state->current_round.rtt_sample_count >= PICOQUIC_HYSTART_PP_N_RTT_SAMPLE) {
if (hystart_pp_state->current_round.current_round_min_rtt < hystart_pp_state->css_baseline_min_rtt) {
fprintf(stdout, "Resume SS.\n");
/* Resume slow start including hystart++. */
hystart_pp_state->css_baseline_min_rtt = UINT64_MAX;
}
}
}
}

uint64_t picoquic_cc_update_target_cwin_estimation(picoquic_path_t* path_x) {
/* RTT measurements will happen after the bandwidth is estimated. */
uint64_t max_win = path_x->peak_bandwidth_estimate * path_x->smoothed_rtt / 1000000;
Expand Down
56 changes: 48 additions & 8 deletions picoquic/cc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ extern "C" {
* HyStart++
*/

/* TODO HyStart++ isn't implemented yet! */
/* It is RECOMMENDED that a HyStart++ implementation use the following constants: */
/* MIN_RTT_THRESH = 4 msec
* MAX_RTT_THRESH = 16 msec
Expand All @@ -45,7 +44,7 @@ extern "C" {
* CSS_ROUNDS = 5
* L = infinity if paced, L = 8 if non-paced
*/
/* Take a look at the draft for more information. */
/* Take a look at the RFC for more information. */
#define PICOQUIC_HYSTART_PP_MIN_RTT_THRESH 4000 /* msec */
#define PICOQUIC_HYSTART_PP_MAX_RTT_THRESH 16000 /* msec */
#define PICOQUIC_HYSTART_PP_MIN_RTT_DIVISOR 8
Expand Down Expand Up @@ -79,6 +78,21 @@ uint64_t picoquic_cc_get_ack_number(picoquic_cnx_t* cnx, picoquic_path_t * path_

uint64_t picoquic_cc_get_ack_sent_time(picoquic_cnx_t* cnx, picoquic_path_t* path_x);

/*
* Slow Start
* Returns number of bytes CWIN should be increased.
*/

uint64_t picoquic_cc_slow_start_increase(picoquic_path_t* path_x, uint64_t nb_delivered);

uint64_t picoquic_cc_slow_start_increase_ex(picoquic_path_t* path_x, uint64_t nb_delivered, int in_css);

uint64_t picoquic_cc_slow_start_increase_ex2(picoquic_path_t* path_x, uint64_t nb_delivered, int in_css, uint64_t prague_alpha);

/*
* HyStart
*/

void picoquic_cc_filter_rtt_min_max(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt);

int picoquic_cc_hystart_loss_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t lost_packet_number, double error_rate_max);
Expand All @@ -88,14 +102,40 @@ int picoquic_cc_hystart_loss_volume_test(picoquic_min_max_rtt_t* rtt_track, pico
int picoquic_cc_hystart_test(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt_measurement, uint64_t packet_time, uint64_t current_time, int is_one_way_delay_enabled);

/*
* Slow Start
* Returns number of bytes CWIN should be increased.
* HyStart++
*/
uint64_t picoquic_cc_slow_start_increase(picoquic_path_t* path_x, uint64_t nb_delivered);

uint64_t picoquic_cc_slow_start_increase_ex(picoquic_path_t* path_x, uint64_t nb_delivered, int in_css);
#define IS_HYSTART_PP_ENABLED(cnx) (cnx->hystart_alg == PICOQUIC_HYSTART_ALGO_NUMBER_HYSTART_PP)
#define IS_IN_CSS(hystart_pp_state) (hystart_pp_state.css_baseline_min_rtt == UINT64_MAX)

uint64_t picoquic_cc_slow_start_increase_ex2(picoquic_path_t* path_x, uint64_t nb_delivered, int in_css, uint64_t prague_alpha);
typedef struct st_picoquic_hystart_pp_round_t {
uint64_t last_round_min_rtt;
uint64_t current_round_min_rtt;
//uint64_t curr_rtt; /* TODO check if needed */
uint64_t rtt_sample_count;
uint64_t window_end;

/* DEBUG. Remove after development finished. */
uint64_t start_time;
uint64_t window_start;
} picoquic_hystart_pp_round_t;

typedef struct st_picoquic_hystart_pp_state_t {
picoquic_hystart_pp_round_t current_round;

uint64_t rtt_thresh;
uint64_t css_baseline_min_rtt;
uint64_t css_round_count;
} picoquic_hystart_pp_state_t;


void picoquic_hystart_pp_reset(picoquic_hystart_pp_state_t* hystart_pp_state);

void picoquic_hystart_pp_start_new_round(picoquic_hystart_pp_state_t* hystart_pp_state, picoquic_cnx_t* cnx, picoquic_path_t* path_x);

void picoquic_hystart_pp_keep_track(picoquic_hystart_pp_state_t* hystart_pp_state, uint64_t rtt_measurement);

void picoquic_hystart_pp_test(picoquic_hystart_pp_state_t *hystart_pp_state);

/*
* Returns CWIN based on bandwidth estimation if larger than current CWIN. Otherwise, returns current CWIN.
Expand All @@ -105,7 +145,7 @@ uint64_t picoquic_cc_update_target_cwin_estimation(picoquic_path_t* path_x);
/*
* Returns CWIN for long RTT connections if larger than current CWIN. Otherwise, returns current CWIN.
*/
uint64_t picoquic_cc_update_cwin_for_long_rtt(picoquic_path_t * path_x);
uint64_t picoquic_cc_update_cwin_for_long_rtt(picoquic_path_t* path_x);

/* Many congestion control algorithms run a parallel version of new reno in order
* to provide a lower bound estimate of either the congestion window or the
Expand Down
17 changes: 15 additions & 2 deletions picoquic/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ static option_table_line_t option_table[] = {
{ picoquic_option_SSLKEYLOG, '8', "sslkeylog", 0, "", "Enable SSLKEYLOG" },
#endif
{ picoquic_option_AddressDiscovery, 'J', "addr_disc", 1, "mode", "provider (0), receiver (1) or both (2)."},
{ picoquic_option_HYSTART, 'H', "hystart", 1, "number", "Enable HyStart. (0 = disabled, 1 = HyStart (default), 2 = HyStart++)"},
{ picoquic_option_HELP, 'h', "help", 0, "", "This help message" }
};

Expand Down Expand Up @@ -437,9 +438,18 @@ static int config_set_option(option_table_line_t* option_desc, option_param_t* p
}
break;
}
case picoquic_option_HELP:
ret = -1;
case picoquic_option_HYSTART: {
int v = config_atoi(params, nb_params, 0, &ret);
if (ret != 0 || v < 0 || v > (int)picoquic_hystart_hystart_pp) {
fprintf(stderr, "Invalid HyStart algorithm: %s\n", config_optval_param_string(opval_buffer, 256, params, nb_params, 0));
ret = (ret == 0) ? -1 : ret;
}
else {
config->hystart_algorithm = (picoquic_hystart_algorithm_enum)v;
}
break;
}
case picoquic_option_HELP:
default:
ret = -1;
break;
Expand Down Expand Up @@ -839,6 +849,8 @@ picoquic_quic_t* picoquic_create_and_configure(picoquic_quic_config_t* config,

picoquic_set_log_level(quic, config->use_long_log);

picoquic_set_default_hystart_algorithm(quic, config->hystart_algorithm);

picoquic_set_preemptive_repeat_policy(quic, config->do_preemptive_repeat);

picoquic_disable_port_blocking(quic, config->disable_port_blocking);
Expand Down Expand Up @@ -897,6 +909,7 @@ void picoquic_config_init(picoquic_quic_config_t* config)
config->initial_random = 3;
config->cwin_max = UINT64_MAX;
config->idle_timeout = PICOQUIC_MICROSEC_HANDSHAKE_MAX / 1000;
config->hystart_algorithm = PICOQUIC_DEFAULT_HYSTART_ALGORITHM; /* Set HyStart as default HyStart algorithm. */
}

void picoquic_config_clear(picoquic_quic_config_t* config)
Expand Down
Loading
Loading