// change to congestion avoidance state.
cubic.set_ssthresh(1);
letmut now = now(); let start_time = now; // helper variables to remember the next packet number to be sent/acked. letmut next_pn_send = 0; letmut next_pn_ack = 0;
// This will start with TCP phase. // in this phase cwnd is increase by CUBIC_ALPHA every RTT. We can look at it as // increase of MAX_DATAGRAM_SIZE every 1 / CUBIC_ALPHA RTTs. // The phase will end when cwnd calculated with cubic equation is equal to TCP estimate: // Cubic::C * (n * RTT / Cubic::ALPHA)^3 * MAX_DATAGRAM_SIZE = n * MAX_DATAGRAM_SIZE // from this n = sqrt(Cubic::ALPHA^3/ (Cubic::C * RTT^3)). let num_tcp_increases = (Cubic::ALPHA.powi(3) / (Cubic::C * RTT.as_secs_f64().powi(3)))
.sqrt()
.floor() as u64;
for _ in0..num_tcp_increases { let cwnd_rtt_start = cubic.cwnd(); // Expected acks during a period of RTT / Cubic::ALPHA. let acks = expected_tcp_acks(cwnd_rtt_start, cubic.max_datagram_size()); // The time between acks if they are ideally paced over a RTT. let time_increase =
RTT / u32::try_from(cwnd_rtt_start / cubic.max_datagram_size()).unwrap();
// The next increase will be according to the cubic equation.
let cwnd_rtt_start = cubic.cwnd(); // cwnd_rtt_start has change, therefore calculate new time_increase (the time // between acks if they are ideally paced over a RTT). let time_increase = RTT / u32::try_from(cwnd_rtt_start / cubic.max_datagram_size()).unwrap(); letmut num_acks = 0; // count the number of acks. until cwnd is increased by cubic.max_datagram_size().
// Make sure that the increase is not according to TCP equation, i.e., that it took // less than RTT / Cubic::ALPHA. let expected_ack_tcp_increase = expected_tcp_acks(cwnd_rtt_start, cubic.max_datagram_size());
assert!(num_acks < expected_ack_tcp_increase);
// This first increase after a TCP phase may be shorter than what it would take by a regular // cubic phase, because of the proper byte counting and the credit it already had before // entering this phase. Therefore We will perform another round and compare it to expected // increase using the cubic equation.
let cwnd_rtt_start_after_tcp = cubic.cwnd(); let elapsed_time = now - start_time;
// calculate new time_increase. let time_increase =
RTT / u32::try_from(cwnd_rtt_start_after_tcp / cubic.max_datagram_size()).unwrap(); letmut num_acks2 = 0; // count the number of acks. until cwnd is increased by MAX_DATAGRAM_SIZE.
let expected_ack_tcp_increase2 =
expected_tcp_acks(cwnd_rtt_start_after_tcp, cubic.max_datagram_size());
assert!(num_acks2 < expected_ack_tcp_increase2);
// The time needed to increase cwnd by MAX_DATAGRAM_SIZE using the cubic equation will be // calculated from: W_cubic(elapsed_time + t_to_increase) - W_cubic(elapsed_time) = // MAX_DATAGRAM_SIZE => Cubic::C * (elapsed_time + t_to_increase)^3 * MAX_DATAGRAM_SIZE + // CWND_INITIAL - Cubic::C * elapsed_time^3 * MAX_DATAGRAM_SIZE + CWND_INITIAL = // MAX_DATAGRAM_SIZE => t_to_increase = cbrt((1 + Cubic::C * elapsed_time^3) / Cubic::C) - // elapsed_time (t_to_increase is in seconds) // number of ack needed is t_to_increase / time_increase. let expected_ack_cubic_increase =
(((Cubic::C.mul_add((elapsed_time).as_secs_f64().powi(3), 1.0) / Cubic::C).cbrt()
- elapsed_time.as_secs_f64())
/ time_increase.as_secs_f64())
.ceil() as u64; // num_acks is very close to the calculated value. The exact value is hard to calculate // because the proportional increase (i.e. curr_cwnd_f64 / (target - curr_cwnd_f64) * // MAX_DATAGRAM_SIZE_F64) and the byte counting.
assert_eq!(num_acks2, expected_ack_cubic_increase + 2);
}
#[test] fn cubic_phase() { letmut cubic = make_cc_cubic(); letmut cc_stats = CongestionControlStats::default(); let cwnd_initial_f64 = convert_to_f64(cubic.cwnd_initial()); // Set w_max to a higher number make sure that cc is the cubic phase (cwnd is calculated // by the cubic equation).
cubic
.congestion_control_mut()
.set_w_max(cwnd_initial_f64 * 10.0); // Set ssthresh to something small to make sure that cc is in the congection avoidance phase.
cubic.set_ssthresh(1); letmut now = now(); letmut next_pn_send = 0; letmut next_pn_ack = 0;
let k = (cwnd_initial_f64.mul_add(10.0, -cwnd_initial_f64)
/ Cubic::C
/ convert_to_f64(cubic.max_datagram_size()))
.cbrt(); let epoch_start = now;
// The number of RTT until W_max is reached. let num_rtts_w_max = (k / RTT.as_secs_f64()).round() as u64; for _ in0..num_rtts_w_max { let cwnd_rtt_start = cubic.cwnd(); // Expected acks let acks = cwnd_rtt_start / cubic.max_datagram_size(); let time_increase = RTT / u32::try_from(acks).unwrap(); for _ in0..acks {
now += time_increase;
ack_packet(&mut cubic, next_pn_ack, now, &mut cc_stats);
next_pn_ack += 1;
next_pn_send = fill_cwnd(&mut cubic, next_pn_send, now);
}
let expected = (Cubic::C * ((now - epoch_start).as_secs_f64() - k).powi(3))
.mul_add(
convert_to_f64(cubic.max_datagram_size()),
cwnd_initial_f64 * 10.0,
)
.round() as usize;
let acked_bytes_before = cubic.acked_bytes();
assert!(acked_bytes_before > 0);
// Trigger the congestion event.
trigger(&mut cubic, now, &mut cc_stats);
// Verify acked_bytes was reduced by the correct factor. let expected = acked_bytes_before * beta / Cubic::BETA_USIZE_DIVISOR;
assert_eq!(cubic.acked_bytes(), expected);
}
// Set ssthresh to something small to make sure that cc is in the congection avoidance phase.
cubic.set_ssthresh(1);
// Set w_max to something higher than cwnd so that the fast convergence is triggered. let cwnd_initial_f64 = convert_to_f64(cubic.cwnd_initial());
cubic
.congestion_control_mut()
.set_w_max(cwnd_initial_f64 * 10.0);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.