5. Estimating the Round-Trip Time
At a high level, an endpoint measures the time from when a packet was sent to when it is acknowledged as an RTT sample. The endpoint uses RTT samples and peer-reported host delays (see Section 13.2 of [QUIC-TRANSPORT]) to generate a statistical description of the network path's RTT. An endpoint computes the following three values for each path: the minimum value over a period of time (min_rtt), an exponentially weighted moving average (smoothed_rtt), and the mean deviation (referred to as "variation" in the rest of this document) in the observed RTT samples (rttvar).
5.1. Generating RTT Samples
An endpoint generates an RTT sample on receiving an ACK frame that meets the following two conditions:
-
the largest acknowledged packet number is newly acknowledged, and
-
at least one of the newly acknowledged packets was ack-eliciting.
The RTT sample, latest_rtt, is generated as the time elapsed since the largest acknowledged packet was sent:
latest_rtt = ack_time - send_time_of_largest_acked
An RTT sample is generated using only the largest acknowledged packet in the received ACK frame. This is because a peer reports acknowledgment delays for only the largest acknowledged packet in an ACK frame. While the reported acknowledgment delay is not used by the RTT sample measurement, it is used to adjust the RTT sample in subsequent computations of smoothed_rtt and rttvar (Section 5.3).
To avoid generating multiple RTT samples for a single packet, an ACK frame SHOULD NOT be used to update RTT estimates if it does not newly acknowledge the largest acknowledged packet.
An RTT sample MUST NOT be generated on receiving an ACK frame that does not newly acknowledge at least one ack-eliciting packet. A peer usually does not send an ACK frame when only non-ack-eliciting packets are received. Therefore, an ACK frame that contains acknowledgments for only non-ack-eliciting packets could include an arbitrarily large ACK Delay value. Ignoring such ACK frames avoids complications in subsequent smoothed_rtt and rttvar computations.
A sender might generate multiple RTT samples per RTT when multiple ACK frames are received within an RTT. As suggested in [RFC6298], doing so might result in inadequate history in smoothed_rtt and rttvar. Ensuring that RTT estimates retain sufficient history is an open research question.
5.2. Estimating min_rtt
min_rtt is the sender's estimate of the minimum RTT observed for a given network path over a period of time. In this document, min_rtt is used by loss detection to reject implausibly small RTT samples.
min_rtt MUST be set to the latest_rtt on the first RTT sample. min_rtt MUST be set to the lesser of min_rtt and latest_rtt (Section 5.1) on all other samples.
An endpoint uses only locally observed times in computing the min_rtt and does not adjust for acknowledgment delays reported by the peer. Doing so allows the endpoint to set a lower bound for the smoothed_rtt based entirely on what it observes (see Section 5.3) and limits potential underestimation due to erroneously reported delays by the peer.
The RTT for a network path may change over time. If a path's actual RTT decreases, the min_rtt will adapt immediately on the first low sample. If the path's actual RTT increases, however, the min_rtt will not adapt to it, allowing future RTT samples that are smaller than the new RTT to be included in smoothed_rtt.
Endpoints SHOULD set the min_rtt to the newest RTT sample after persistent congestion is established. This avoids repeatedly declaring persistent congestion when the RTT increases. This also allows a connection to reset its estimate of min_rtt and smoothed_rtt after a disruptive network event; see Section 5.3.
Endpoints MAY reestablish the min_rtt at other times in the connection, such as when traffic volume is low and an acknowledgment is received with a low acknowledgment delay. Implementations SHOULD NOT refresh the min_rtt value too often since the actual minimum RTT of the path is not frequently observable.
5.3. Estimating smoothed_rtt and rttvar
smoothed_rtt is an exponentially weighted moving average of an endpoint's RTT samples, and rttvar estimates the variation in the RTT samples using a mean variation.
The calculation of smoothed_rtt uses RTT samples after adjusting them for acknowledgment delays. These delays are decoded from the ACK Delay field of ACK frames as described in Section 19.3 of [QUIC-TRANSPORT].
The peer might report acknowledgment delays that are larger than the peer's max_ack_delay during the handshake (Section 13.2.1 of [QUIC-TRANSPORT]). To account for this, the endpoint SHOULD ignore max_ack_delay until the handshake is confirmed, as defined in Section 4.1.2 of [QUIC-TLS]. When they occur, these large acknowledgment delays are likely to be non-repeating and limited to the handshake. The endpoint can therefore use them without limiting them to the max_ack_delay, avoiding unnecessary inflation of the RTT estimate.
Note that a large acknowledgment delay can result in a substantially inflated smoothed_rtt if there is an error either in the peer's reporting of the acknowledgment delay or in the endpoint's min_rtt estimate. Therefore, prior to handshake confirmation, an endpoint MAY ignore RTT samples if adjusting the RTT sample for acknowledgment delay causes the sample to be less than the min_rtt.
After the handshake is confirmed, any acknowledgment delays reported by the peer that are greater than the peer's max_ack_delay are attributed to unintentional but potentially repeating delays, such as scheduler latency at the peer or loss of previous acknowledgments. Excess delays could also be due to a noncompliant receiver. Therefore, these extra delays are considered effectively part of path delay and incorporated into the RTT estimate.
Therefore, when adjusting an RTT sample using peer-reported acknowledgment delays, an endpoint:
-
MAY ignore the acknowledgment delay for Initial packets, since these acknowledgments are not delayed by the peer (Section 13.2.1 of [QUIC-TRANSPORT]);
-
SHOULD ignore the peer's max_ack_delay until the handshake is confirmed;
-
MUST use the lesser of the acknowledgment delay and the peer's max_ack_delay after the handshake is confirmed; and
-
MUST NOT subtract the acknowledgment delay from the RTT sample if the resulting value is smaller than the min_rtt. This limits the underestimation of the smoothed_rtt due to a misreporting peer.
Additionally, an endpoint might postpone the processing of acknowledgments when the corresponding decryption keys are not immediately available. For example, a client might receive an acknowledgment for a 0-RTT packet that it cannot decrypt because 1-RTT packet protection keys are not yet available to it. In such cases, an endpoint SHOULD subtract such local delays from its RTT sample until the handshake is confirmed.
Similar to [RFC6298], smoothed_rtt and rttvar are computed as follows.
An endpoint initializes the RTT estimator during connection establishment and when the estimator is reset during connection migration; see Section 9.4 of [QUIC-TRANSPORT]. Before any RTT samples are available for a new path or when the estimator is reset, the estimator is initialized using the initial RTT; see Section 6.2.2.
smoothed_rtt and rttvar are initialized as follows, where kInitialRtt contains the initial RTT value:
smoothed_rtt = kInitialRtt
rttvar = kInitialRtt / 2
RTT samples for the network path are recorded in latest_rtt; see Section 5.1. On the first RTT sample after initialization, the estimator is reset using that sample. This ensures that the estimator retains no history of past samples. Packets sent on other paths do not contribute RTT samples to the current path, as described in Section 9.4 of [QUIC-TRANSPORT].
On the first RTT sample after initialization, smoothed_rtt and rttvar are set as follows:
smoothed_rtt = latest_rtt
rttvar = latest_rtt / 2
On subsequent RTT samples, smoothed_rtt and rttvar evolve as follows:
ack_delay = decoded acknowledgment delay from ACK frame
if (handshake confirmed):
ack_delay = min(ack_delay, max_ack_delay)
adjusted_rtt = latest_rtt
if (latest_rtt >= min_rtt + ack_delay):
adjusted_rtt = latest_rtt - ack_delay
smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt
rttvar_sample = abs(smoothed_rtt - adjusted_rtt)
rttvar = 3/4 * rttvar + 1/4 * rttvar_sample