// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ #define QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_ #include "quiche/quic/core/congestion_control/send_algorithm_interface.h" #include "quiche/quic/core/congestion_control/windowed_filter.h" #include "quiche/quic/core/packet_number_indexed_queue.h" #include "quiche/quic/core/quic_bandwidth.h" #include "quiche/quic/core/quic_packet_number.h" #include "quiche/quic/core/quic_packets.h" #include "quiche/quic/core/quic_time.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/core/quic_unacked_packet_map.h" #include "quiche/quic/platform/api/quic_export.h" #include "quiche/quic/platform/api/quic_flags.h" #include "quiche/common/quiche_circular_deque.h" namespace quic { namespace test { class BandwidthSamplerPeer; } // namespace test // A subset of BandwidthSampler::ConnectionStateOnSentPacket which is returned // to the caller when the packet is acked or lost. struct QUICHE_EXPORT SendTimeState { … }; struct QUICHE_EXPORT ExtraAckedEvent { … }; struct QUICHE_EXPORT BandwidthSample { … }; // MaxAckHeightTracker is part of the BandwidthSampler. It is called after every // ack event to keep track the degree of ack aggregation(a.k.a "ack height"). class QUICHE_EXPORT MaxAckHeightTracker { … }; // An interface common to any class that can provide bandwidth samples from the // information per individual acknowledged packet. class QUICHE_EXPORT BandwidthSamplerInterface { … }; // BandwidthSampler keeps track of sent and acknowledged packets and outputs a // bandwidth sample for every packet acknowledged. The samples are taken for // individual packets, and are not filtered; the consumer has to filter the // bandwidth samples itself. In certain cases, the sampler will locally severely // underestimate the bandwidth, hence a maximum filter with a size of at least // one RTT is recommended. // // This class bases its samples on the slope of two curves: the number of bytes // sent over time, and the number of bytes acknowledged as received over time. // It produces a sample of both slopes for every packet that gets acknowledged, // based on a slope between two points on each of the corresponding curves. Note // that due to the packet loss, the number of bytes on each curve might get // further and further away from each other, meaning that it is not feasible to // compare byte values coming from different curves with each other. // // The obvious points for measuring slope sample are the ones corresponding to // the packet that was just acknowledged. Let us denote them as S_1 (point at // which the current packet was sent) and A_1 (point at which the current packet // was acknowledged). However, taking a slope requires two points on each line, // so estimating bandwidth requires picking a packet in the past with respect to // which the slope is measured. // // For that purpose, BandwidthSampler always keeps track of the most recently // acknowledged packet, and records it together with every outgoing packet. // When a packet gets acknowledged (A_1), it has not only information about when // it itself was sent (S_1), but also the information about a previously // acknowledged packet before it was sent (S_0 and A_0). // // Based on that data, send and ack rate are estimated as: // send_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0)) // ack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0)) // // Here, the ack rate is intuitively the rate we want to treat as bandwidth. // However, in certain cases (e.g. ack compression) the ack rate at a point may // end up higher than the rate at which the data was originally sent, which is // not indicative of the real bandwidth. Hence, we use the send rate as an upper // bound, and the sample value is // rate_sample = min(send_rate, ack_rate) // // An important edge case handled by the sampler is tracking the app-limited // samples. There are multiple meaning of "app-limited" used interchangeably, // hence it is important to understand and to be able to distinguish between // them. // // Meaning 1: connection state. The connection is said to be app-limited when // there is no outstanding data to send. This means that certain bandwidth // samples in the future would not be an accurate indication of the link // capacity, and it is important to inform consumer about that. Whenever // connection becomes app-limited, the sampler is notified via OnAppLimited() // method. // // Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth // sampler becomes notified about the connection being app-limited, it enters // app-limited phase. In that phase, all *sent* packets are marked as // app-limited. Note that the connection itself does not have to be // app-limited during the app-limited phase, and in fact it will not be // (otherwise how would it send packets?). The boolean flag below indicates // whether the sampler is in that phase. // // Meaning 3: a flag on the sent packet and on the sample. If a sent packet is // sent during the app-limited phase, the resulting sample related to the // packet will be marked as app-limited. // // With the terminology issue out of the way, let us consider the question of // what kind of situation it addresses. // // Consider a scenario where we first send packets 1 to 20 at a regular // bandwidth, and then immediately run out of data. After a few seconds, we send // packets 21 to 60, and only receive ack for 21 between sending packets 40 and // 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0 // we use to compute the slope is going to be packet 20, a few seconds apart // from the current packet, hence the resulting estimate would be extremely low // and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21, // meaning that the bandwidth sample would exclude the quiescence. // // Based on the analysis of that scenario, we implement the following rule: once // OnAppLimited() is called, all sent packets will produce app-limited samples // up until an ack for a packet that was sent after OnAppLimited() was called. // Note that while the scenario above is not the only scenario when the // connection is app-limited, the approach works in other cases too. class QUICHE_EXPORT BandwidthSampler : public BandwidthSamplerInterface { … }; } // namespace quic #endif // QUICHE_QUIC_CORE_CONGESTION_CONTROL_BANDWIDTH_SAMPLER_H_