Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(D)CUBIC, NewReno and Prague refactoring #1818

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions PerfAndStressTest/PerfAndStressTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ namespace PerfAndStressTest
Assert::AreEqual(ret, 0);
}

TEST_METHOD(satellite_dcubic_seed)
{
int ret = satellite_dcubic_seeded_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(satellite_prague_seed)
{
int ret = satellite_prague_seeded_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(bdp_basic)
{
int ret = bdp_basic_test();
Expand Down
6 changes: 3 additions & 3 deletions picoquic/bbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2008,15 +2008,15 @@ void BBRCheckStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* pa
return;
}

if (picoquic_hystart_test(&bbr_state->rtt_filter, rs->rtt_sample,
if (picoquic_cc_hystart_test(&bbr_state->rtt_filter, rs->rtt_sample,
path_x->pacing.packet_time_microsec, current_time, 0)) {
BBRExitStartupLongRtt(bbr_state, path_x, current_time);
}
else if (rs->ecn_alpha > BBRExcessiveEcnCE) {
BBRExitStartupLongRtt(bbr_state, path_x, current_time);
}
else {
int excessive_loss = picoquic_hystart_loss_volume_test(&bbr_state->rtt_filter, picoquic_congestion_notification_repeat,
int excessive_loss = picoquic_cc_hystart_loss_volume_test(&bbr_state->rtt_filter, picoquic_congestion_notification_repeat,
rs->newly_acked, rs->newly_lost);
if (excessive_loss) {
BBRExitStartupLongRtt(bbr_state, path_x, current_time);
Expand All @@ -2027,7 +2027,7 @@ void BBRCheckStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* pa
void BBRUpdateStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, bbr_per_ack_state_t* rs, uint64_t current_time)
{
if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) {
picoquic_hystart_increase(path_x, &bbr_state->rtt_filter, rs->newly_acked);
path_x->cwin += picoquic_cc_slow_start_increase(path_x, rs->newly_acked);
}

uint64_t max_win = path_x->peak_bandwidth_estimate * bbr_state->min_rtt / 1000000;
Expand Down
6 changes: 3 additions & 3 deletions picoquic/bbr1.c
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ static void picoquic_bbr1_notify(
case picoquic_congestion_notification_timeout:
/* Non standard code to react to high rate of packet loss, or timeout loss */
if (ack_state->lost_packet_number >= bbr1_state->congestion_sequence &&
picoquic_hystart_loss_test(&bbr1_state->rtt_filter, notification, ack_state->lost_packet_number, 0.20)) {
picoquic_cc_hystart_loss_test(&bbr1_state->rtt_filter, notification, ack_state->lost_packet_number, 0.20)) {
picoquic_bbr1_notify_congestion(bbr1_state, cnx, path_x, current_time,
(notification == picoquic_congestion_notification_timeout) ? 1 : 0);
}
Expand All @@ -1160,7 +1160,7 @@ static void picoquic_bbr1_notify(
}

if (bbr1_state->state == picoquic_bbr1_alg_startup_long_rtt) {
if (picoquic_hystart_test(&bbr1_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
if (picoquic_cc_hystart_test(&bbr1_state->rtt_filter, (cnx->is_time_stamp_enabled) ? ack_state->one_way_delay : ack_state->rtt_measurement,
cnx->path[0]->pacing.packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) {
BBR1ExitStartupLongRtt(bbr1_state, path_x, current_time);
}
Expand All @@ -1177,7 +1177,7 @@ static void picoquic_bbr1_notify(
bbr1_state->rt_prop_stamp = current_time;
}
if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) {
picoquic_hystart_increase(path_x, &bbr1_state->rtt_filter, bbr1_state->bytes_delivered);
path_x->cwin += picoquic_cc_slow_start_increase(path_x, bbr1_state->bytes_delivered);
}
bbr1_state->bytes_delivered = 0;

Expand Down
96 changes: 87 additions & 9 deletions picoquic/cc_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ uint64_t picoquic_cc_get_sequence_number(picoquic_cnx_t* cnx, picoquic_path_t* p
else {
sequence_number = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence;
}

return sequence_number;
}

Expand All @@ -47,6 +48,7 @@ uint64_t picoquic_cc_get_ack_number(picoquic_cnx_t* cnx, picoquic_path_t* path_x
else {
highest_acknowledged = cnx->pkt_ctx[picoquic_packet_context_application].highest_acknowledged;
}

return highest_acknowledged;
}

Expand All @@ -60,16 +62,16 @@ uint64_t picoquic_cc_get_ack_sent_time(picoquic_cnx_t* cnx, picoquic_path_t* pat
else {
latest_time_acknowledged = cnx->pkt_ctx[picoquic_packet_context_application].latest_time_acknowledged;
}

return latest_time_acknowledged;
}


void picoquic_filter_rtt_min_max(picoquic_min_max_rtt_t * rtt_track, uint64_t rtt)
void picoquic_cc_filter_rtt_min_max(picoquic_min_max_rtt_t * rtt_track, uint64_t rtt)
{
int x = rtt_track->sample_current;
int x_max;


rtt_track->samples[x] = rtt;

rtt_track->sample_current = x + 1;
Expand All @@ -92,7 +94,7 @@ void picoquic_filter_rtt_min_max(picoquic_min_max_rtt_t * rtt_track, uint64_t rt
}
}

int picoquic_hystart_loss_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event,
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)
{
int ret = 0;
Expand Down Expand Up @@ -125,7 +127,7 @@ int picoquic_hystart_loss_test(picoquic_min_max_rtt_t* rtt_track, picoquic_conge
return ret;
}

int picoquic_hystart_loss_volume_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t nb_bytes_newly_acked, uint64_t nb_bytes_newly_lost)
int picoquic_cc_hystart_loss_volume_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t nb_bytes_newly_acked, uint64_t nb_bytes_newly_lost)
{
int ret = 0;

Expand Down Expand Up @@ -154,12 +156,12 @@ int picoquic_hystart_loss_volume_test(picoquic_min_max_rtt_t* rtt_track, picoqui
return ret;
}

int picoquic_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)
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)
{
int ret = 0;

if(current_time > rtt_track->last_rtt_sample_time + 1000) {
picoquic_filter_rtt_min_max(rtt_track, rtt_measurement);
picoquic_cc_filter_rtt_min_max(rtt_track, rtt_measurement);
rtt_track->last_rtt_sample_time = current_time;

if (rtt_track->is_init) {
Expand Down Expand Up @@ -189,9 +191,85 @@ int picoquic_hystart_test(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt_measur
return ret;
}

void picoquic_hystart_increase(picoquic_path_t * path_x, picoquic_min_max_rtt_t* rtt_filter, uint64_t nb_delivered)
uint64_t picoquic_cc_slow_start_increase(picoquic_path_t * path_x, uint64_t nb_delivered) {
/* App limited. */
/* TODO discuss
* path_x->cwin < path_x->bytes_in_transit returns false in cc code
* path_x->cnx->cwin_blocked is set to true
* (path_x->cwin < path_x->bytes_in_transit) != path_x->cnx->cwin_blocked?
*/
if (!path_x->cnx->cwin_blocked) {
return 0;
}

return nb_delivered;
}

uint64_t picoquic_cc_slow_start_increase_ex(picoquic_path_t * path_x, uint64_t nb_delivered, int in_css)
{
path_x->cwin += nb_delivered;
if (in_css) {
/* In consecutive Slow Start. */
return picoquic_cc_slow_start_increase(path_x, nb_delivered / PICOQUIC_HYSTART_PP_CSS_GROWTH_DIVISOR);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant for hystart++ support?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is.
I copied a part of my HyStart++ macros/constants to the code for CSS version of the increase function.
So the current code defines the recommended growth divisor (=4, as specified by the HyStart++ RFC) as a macro.
Another option is to change the in_css parameter to a double to specify the divisor from the outside of the function. (more flexible)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My next paper will be about HyStart++. I will share my results here. But I can't tell any date, because I'm very busy with careful resume currently.

/* Fallback to traditional Slow Start. */
return picoquic_cc_slow_start_increase(path_x, nb_delivered); /* nb_delivered; */
}

uint64_t picoquic_cc_slow_start_increase_ex2(picoquic_path_t* path_x, uint64_t nb_delivered, int in_css, uint64_t prague_alpha) {
if (prague_alpha != 0) { /* monitoring of ECN */
uint64_t delta = nb_delivered;

/* Calculate delta based on prague_ahpha. */
if (path_x->smoothed_rtt <= PICOQUIC_TARGET_RENO_RTT) {
/* smoothed_rtt <= 100ms */
delta *= (1024 - prague_alpha);
delta /= 1024;
} else {
delta *= path_x->smoothed_rtt;
delta *= (1024 - prague_alpha);
delta /= PICOQUIC_TARGET_RENO_RTT;
delta /= 1024;
}

return picoquic_cc_slow_start_increase_ex(path_x, delta, in_css);
}

/* Fallback to HyStart++ Consecutive Slow Start. */
return picoquic_cc_slow_start_increase_ex(path_x, nb_delivered, in_css);
}

uint64_t picoquic_cc_bandwidth_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;
uint64_t min_win = max_win / 2;

/* Return increased cwin, if larger than current cwin. */
if (min_win > path_x->cwin) {
return min_win;
}

/* Otherwise, return current cwin. */
return path_x->cwin;
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not estimating bandwidth. It is estimating a value of cwin, similar to the "target cwin" used in BBR. Maybe rename to picoquic_cc_target_cwin_estimation or some such?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true.
I have used the word "increase" for the functions which only return the portion of increased bytes and "update" for functions which return the CWIN.
Is picoquic_cc_update_target_cwin_estimation an option?

uint64_t picoquic_cc_increase_cwin_for_long_rtt(picoquic_path_t * path_x) {
uint64_t min_cwnd;

if (path_x->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) {
min_cwnd = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)PICOQUIC_TARGET_SATELLITE_RTT / (double)PICOQUIC_TARGET_RENO_RTT);
}
else {
min_cwnd = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)path_x->rtt_min / (double)PICOQUIC_TARGET_RENO_RTT);
}

/* Return increased cwin, if larger than current cwin. */
if (min_cwnd > path_x->cwin) {
return min_cwnd;
}

/* Otherwise, return current cwin. */
return path_x->cwin;
}

uint64_t picoquic_cc_increased_window(picoquic_cnx_t* cnx, uint64_t previous_window)
Expand All @@ -203,7 +281,7 @@ uint64_t picoquic_cc_increased_window(picoquic_cnx_t* cnx, uint64_t previous_win
else {
double w = (double)previous_window;
w /= (double)PICOQUIC_TARGET_RENO_RTT;
w *= (cnx->path[0]->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT)? PICOQUIC_TARGET_SATELLITE_RTT: cnx->path[0]->rtt_min;
w *= (cnx->path[0]->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) ? PICOQUIC_TARGET_SATELLITE_RTT : (double)cnx->path[0]->rtt_min;
new_window = (uint64_t)w;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You copied the existing code, and that's fine. But it is a bit funky. It is means to open the window faster when the RTT is long, but since that we came out with "careful resume" that solves a large part of the problem when a previous bandwidth estimate is available, and then with increases based on peak bandwidth estimate, which also provides for rapid growth of cwin in these high delay links. So maybe we should add a note that this code should be revisited and perhaps removed. If we keep it, it would be nice to use integer arithmetic instead of (double).

But let's keep the code as is for now. Maybe add a comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our careful resume (CR) implementation this part of the code is already removed. :)
https://github.com/hfstco/picoquic/tree/cr

We submitted a paper about CR and I think it will be published in March. It focuses on CR and first measurements on satellite connections. (GEO, LEO, ...) I want it to share it, but I think the IEEE copyright forbids me to. Maybe I can share a pre-submitted version of it. I have to take a look.

But yes, these part of code can be removed, but currently don't have to.

}
return new_window;
Expand Down
54 changes: 49 additions & 5 deletions picoquic/cc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,33 @@ extern "C" {
#define PICOQUIC_SMOOTHED_LOSS_FACTOR (1.0/16.0)
#define PICOQUIC_SMOOTHED_LOSS_THRESHOLD (0.15)

/*
* 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
* MIN_RTT_DIVISOR = 8
* N_RTT_SAMPLE = 8
* CSS_GROWTH_DIVISOR = 4
* CSS_ROUNDS = 5
* L = infinity if paced, L = 8 if non-paced
*/
/* Take a look at the draft 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
#define PICOQUIC_HYSTART_PP_N_RTT_SAMPLE 8
#define PICOQUIC_HYSTART_PP_CSS_GROWTH_DIVISOR 4
#define PICOQUIC_HYSTART_PP_CSS_ROUNDS 5
/* Since picoquic is always paced, L is set to infinity (UINT64_MAX).
* Because L is only used to limit the increase function, we don't need it at all. For more information, take a look at
* the picoquic_hystart_pp_increase() function.
*/
/* #define PICOQUIC_HYSTART_PP_L UINT64_MAX */ /* infinity if paced, L = 8 if non-paced */

typedef struct st_picoquic_min_max_rtt_t {
uint64_t last_rtt_sample_time;
uint64_t rtt_filtered_min;
Expand All @@ -52,15 +79,32 @@ 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);

void picoquic_filter_rtt_min_max(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt);
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);

int picoquic_cc_hystart_loss_volume_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t nb_bytes_newly_acked, uint64_t nb_bytes_newly_lost);

int picoquic_hystart_loss_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t lost_packet_number, double error_rate_max);
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);

int picoquic_hystart_loss_volume_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t nb_bytes_newly_acked, uint64_t nb_bytes_newly_lost);
/*
* Slow Start
*/
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);

int picoquic_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);
/*
* Increase cwin based on bandwidth estimation.
*/
uint64_t picoquic_cc_bandwidth_estimation(picoquic_path_t* path_x);

void picoquic_hystart_increase(picoquic_path_t* path_x, picoquic_min_max_rtt_t* rtt_filter, uint64_t nb_delivered);
/*
* Increase cwin for long RTT connections.
*/
uint64_t picoquic_cc_increase_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
Loading
Loading