[
HyStart] specifies two algorithms (a "Delay Increase" algorithm and an "Inter-Packet Arrival" algorithm) to be run in parallel to detect that the sending rate has reached capacity. In practice, the Inter-Packet Arrival algorithm does not perform well and is not able to detect congestion early, primarily due to ACK compression. The idea of the Delay Increase algorithm is to look for spikes in RTT (round-trip time), which suggest that the bottleneck buffer is filling up.
In HyStart++, a TCP sender uses standard slow start and then uses the Delay Increase algorithm to trigger an exit from slow start. But instead of going straight from slow start to congestion avoidance, the sender spends a number of RTTs in a Conservative Slow Start (CSS) phase to determine whether the exit from slow start was premature. During CSS, the congestion window is grown exponentially in a fashion similar to regular slow start, but with a smaller exponential base, resulting in less aggressive growth. If the RTT reduces during CSS, it's concluded that the RTT spike was not related to congestion caused by the connection sending at a rate greater than the ideal send rate, and the connection resumes slow start. If the RTT inflation persists throughout CSS, the connection enters congestion avoidance.
The following pseudocode uses a limit, L, to control the aggressiveness of the cwnd increase during both standard slow start and CSS. While an arriving ACK may newly acknowledge an arbitrary number of bytes, the HyStart++ algorithm limits the number of those bytes applied to increase the cwnd to L*SMSS bytes.
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
HyStart++ measures rounds using sequence numbers, as follows:
-
Define windowEnd as a sequence number initialized to SND.NXT.
-
When windowEnd is ACKed, the current round ends and windowEnd is set to SND.NXT.
At the start of each round during standard slow start [
RFC 5681] and CSS, initialize the variables used to compute the last round's and current round's minimum RTT:
lastRoundMinRTT = currentRoundMinRTT
currentRoundMinRTT = infinity
rttSampleCount = 0
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)
Keep track of the minimum observed RTT:
currentRoundMinRTT = min(currentRoundMinRTT, currRTT)
rttSampleCount += 1
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 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)
Keep track of the minimum observed RTT:
currentRoundMinRTT = min(currentRoundMinRTT, currRTT)
rttSampleCount += 1
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++
CSS lasts at most CSS_ROUNDS rounds. If the transition into CSS happens in the middle of a round, that partial round counts towards the limit.
If CSS_ROUNDS rounds are complete, enter congestion avoidance by setting the ssthresh to the current cwnd.
If loss or Explicit Congestion Notification (ECN) marking is observed at any time during standard slow start or CSS, enter congestion avoidance by setting the ssthresh to the current cwnd.
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
These constants have been determined with lab measurements and real-world deployments. An implementation
MAY tune them for different network characteristics.
The delay increase sensitivity is determined by MIN_RTT_THRESH and MAX_RTT_THRESH. Smaller values of MIN_RTT_THRESH may cause spurious exits from slow start. Larger values of MAX_RTT_THRESH may result in slow start not exiting until loss is encountered for connections on large RTT paths.
MIN_RTT_DIVISOR is a fraction of RTT to compute the delay threshold. A smaller value would mean a larger threshold and thus less sensitivity to delay increase, and vice versa.
While all TCP implementations are
REQUIRED to take at least one RTT sample each round, implementations of HyStart++ are
RECOMMENDED to take at least N_RTT_SAMPLE RTT samples. Using lower values of N_RTT_SAMPLE will lower the accuracy of the measured RTT for the round; higher values will improve accuracy at the cost of more processing.
The minimum value of CSS_GROWTH_DIVISOR
MUST be at least 2. A value of 1 results in the same aggressive behavior as regular slow start. Values larger than 4 will cause the algorithm to be less aggressive and maybe less performant.
Smaller values of CSS_ROUNDS may miss detecting jitter, and larger values may limit performance.
Packet pacing [
ASA00] is a possible mechanism to avoid large bursts and their associated harm. A paced TCP implementation
SHOULD use L = infinity. Burst concerns are mitigated by pacing, and this setting allows for optimal cwnd growth on modern networks.
For TCP implementations that pace to mitigate burst concerns, L values smaller than infinity may suffer performance problems due to slow cwnd growth in high-speed networks. For non-paced TCP implementations, L values smaller than 8 may suffer performance problems due to slow cwnd growth in high-speed networks; L values larger than 8 may cause an increase in burstiness and thereby loss rates, and result in poor performance.
An implementation
SHOULD use HyStart++ only for the initial slow start (when the ssthresh is at its initial value of arbitrarily high per [
RFC 5681]) and fall back to using standard slow start for the remainder of the connection lifetime. This is acceptable because subsequent slow starts will use the discovered ssthresh value to exit slow start and avoid the overshoot problem. An implementation
MAY use HyStart++ to grow the restart window [
RFC 5681] after a long idle period.
In application-limited scenarios, the amount of data in flight could fall below the bandwidth-delay product (BDP) and result in smaller RTT samples, which can trigger an exit back to slow start. It is expected that a connection might oscillate between CSS and slow start in such scenarios. But this behavior will neither result in a connection prematurely entering congestion avoidance nor cause overshooting compared to slow start.