bandwidth_sampler.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. package congestion
  2. import (
  3. "math"
  4. "time"
  5. "github.com/sagernet/quic-go/congestion"
  6. )
  7. var InfiniteBandwidth = Bandwidth(math.MaxUint64)
  8. // SendTimeState is a subset of ConnectionStateOnSentPacket which is returned
  9. // to the caller when the packet is acked or lost.
  10. type SendTimeState struct {
  11. // Whether other states in this object is valid.
  12. isValid bool
  13. // Whether the sender is app limited at the time the packet was sent.
  14. // App limited bandwidth sample might be artificially low because the sender
  15. // did not have enough data to send in order to saturate the link.
  16. isAppLimited bool
  17. // Total number of sent bytes at the time the packet was sent.
  18. // Includes the packet itself.
  19. totalBytesSent congestion.ByteCount
  20. // Total number of acked bytes at the time the packet was sent.
  21. totalBytesAcked congestion.ByteCount
  22. // Total number of lost bytes at the time the packet was sent.
  23. totalBytesLost congestion.ByteCount
  24. }
  25. // ConnectionStateOnSentPacket represents the information about a sent packet
  26. // and the state of the connection at the moment the packet was sent,
  27. // specifically the information about the most recently acknowledged packet at
  28. // that moment.
  29. type ConnectionStateOnSentPacket struct {
  30. packetNumber congestion.PacketNumber
  31. // Time at which the packet is sent.
  32. sendTime time.Time
  33. // Size of the packet.
  34. size congestion.ByteCount
  35. // The value of |totalBytesSentAtLastAckedPacket| at the time the
  36. // packet was sent.
  37. totalBytesSentAtLastAckedPacket congestion.ByteCount
  38. // The value of |lastAckedPacketSentTime| at the time the packet was
  39. // sent.
  40. lastAckedPacketSentTime time.Time
  41. // The value of |lastAckedPacketAckTime| at the time the packet was
  42. // sent.
  43. lastAckedPacketAckTime time.Time
  44. // Send time states that are returned to the congestion controller when the
  45. // packet is acked or lost.
  46. sendTimeState SendTimeState
  47. }
  48. // BandwidthSample
  49. type BandwidthSample struct {
  50. // The bandwidth at that particular sample. Zero if no valid bandwidth sample
  51. // is available.
  52. bandwidth Bandwidth
  53. // The RTT measurement at this particular sample. Zero if no RTT sample is
  54. // available. Does not correct for delayed ack time.
  55. rtt time.Duration
  56. // States captured when the packet was sent.
  57. stateAtSend SendTimeState
  58. }
  59. func NewBandwidthSample() *BandwidthSample {
  60. return &BandwidthSample{
  61. // FIXME: the default value of original code is zero.
  62. rtt: InfiniteRTT,
  63. }
  64. }
  65. // BandwidthSampler keeps track of sent and acknowledged packets and outputs a
  66. // bandwidth sample for every packet acknowledged. The samples are taken for
  67. // individual packets, and are not filtered; the consumer has to filter the
  68. // bandwidth samples itself. In certain cases, the sampler will locally severely
  69. // underestimate the bandwidth, hence a maximum filter with a size of at least
  70. // one RTT is recommended.
  71. //
  72. // This class bases its samples on the slope of two curves: the number of bytes
  73. // sent over time, and the number of bytes acknowledged as received over time.
  74. // It produces a sample of both slopes for every packet that gets acknowledged,
  75. // based on a slope between two points on each of the corresponding curves. Note
  76. // that due to the packet loss, the number of bytes on each curve might get
  77. // further and further away from each other, meaning that it is not feasible to
  78. // compare byte values coming from different curves with each other.
  79. //
  80. // The obvious points for measuring slope sample are the ones corresponding to
  81. // the packet that was just acknowledged. Let us denote them as S_1 (point at
  82. // which the current packet was sent) and A_1 (point at which the current packet
  83. // was acknowledged). However, taking a slope requires two points on each line,
  84. // so estimating bandwidth requires picking a packet in the past with respect to
  85. // which the slope is measured.
  86. //
  87. // For that purpose, BandwidthSampler always keeps track of the most recently
  88. // acknowledged packet, and records it together with every outgoing packet.
  89. // When a packet gets acknowledged (A_1), it has not only information about when
  90. // it itself was sent (S_1), but also the information about the latest
  91. // acknowledged packet right before it was sent (S_0 and A_0).
  92. //
  93. // Based on that data, send and ack rate are estimated as:
  94. //
  95. // send_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0))
  96. // ack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0))
  97. //
  98. // Here, the ack rate is intuitively the rate we want to treat as bandwidth.
  99. // However, in certain cases (e.g. ack compression) the ack rate at a point may
  100. // end up higher than the rate at which the data was originally sent, which is
  101. // not indicative of the real bandwidth. Hence, we use the send rate as an upper
  102. // bound, and the sample value is
  103. //
  104. // rate_sample = min(send_rate, ack_rate)
  105. //
  106. // An important edge case handled by the sampler is tracking the app-limited
  107. // samples. There are multiple meaning of "app-limited" used interchangeably,
  108. // hence it is important to understand and to be able to distinguish between
  109. // them.
  110. //
  111. // Meaning 1: connection state. The connection is said to be app-limited when
  112. // there is no outstanding data to send. This means that certain bandwidth
  113. // samples in the future would not be an accurate indication of the link
  114. // capacity, and it is important to inform consumer about that. Whenever
  115. // connection becomes app-limited, the sampler is notified via OnAppLimited()
  116. // method.
  117. //
  118. // Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth
  119. // sampler becomes notified about the connection being app-limited, it enters
  120. // app-limited phase. In that phase, all *sent* packets are marked as
  121. // app-limited. Note that the connection itself does not have to be
  122. // app-limited during the app-limited phase, and in fact it will not be
  123. // (otherwise how would it send packets?). The boolean flag below indicates
  124. // whether the sampler is in that phase.
  125. //
  126. // Meaning 3: a flag on the sent packet and on the sample. If a sent packet is
  127. // sent during the app-limited phase, the resulting sample related to the
  128. // packet will be marked as app-limited.
  129. //
  130. // With the terminology issue out of the way, let us consider the question of
  131. // what kind of situation it addresses.
  132. //
  133. // Consider a scenario where we first send packets 1 to 20 at a regular
  134. // bandwidth, and then immediately run out of data. After a few seconds, we send
  135. // packets 21 to 60, and only receive ack for 21 between sending packets 40 and
  136. // 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0
  137. // we use to compute the slope is going to be packet 20, a few seconds apart
  138. // from the current packet, hence the resulting estimate would be extremely low
  139. // and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21,
  140. // meaning that the bandwidth sample would exclude the quiescence.
  141. //
  142. // Based on the analysis of that scenario, we implement the following rule: once
  143. // OnAppLimited() is called, all sent packets will produce app-limited samples
  144. // up until an ack for a packet that was sent after OnAppLimited() was called.
  145. // Note that while the scenario above is not the only scenario when the
  146. // connection is app-limited, the approach works in other cases too.
  147. type BandwidthSampler struct {
  148. // The total number of congestion controlled bytes sent during the connection.
  149. totalBytesSent congestion.ByteCount
  150. // The total number of congestion controlled bytes which were acknowledged.
  151. totalBytesAcked congestion.ByteCount
  152. // The total number of congestion controlled bytes which were lost.
  153. totalBytesLost congestion.ByteCount
  154. // The value of |totalBytesSent| at the time the last acknowledged packet
  155. // was sent. Valid only when |lastAckedPacketSentTime| is valid.
  156. totalBytesSentAtLastAckedPacket congestion.ByteCount
  157. // The time at which the last acknowledged packet was sent. Set to
  158. // QuicTime::Zero() if no valid timestamp is available.
  159. lastAckedPacketSentTime time.Time
  160. // The time at which the most recent packet was acknowledged.
  161. lastAckedPacketAckTime time.Time
  162. // The most recently sent packet.
  163. lastSendPacket congestion.PacketNumber
  164. // Indicates whether the bandwidth sampler is currently in an app-limited
  165. // phase.
  166. isAppLimited bool
  167. // The packet that will be acknowledged after this one will cause the sampler
  168. // to exit the app-limited phase.
  169. endOfAppLimitedPhase congestion.PacketNumber
  170. // Record of the connection state at the point where each packet in flight was
  171. // sent, indexed by the packet number.
  172. connectionStats *ConnectionStates
  173. }
  174. func NewBandwidthSampler() *BandwidthSampler {
  175. return &BandwidthSampler{
  176. connectionStats: &ConnectionStates{
  177. stats: make(map[congestion.PacketNumber]*ConnectionStateOnSentPacket),
  178. },
  179. }
  180. }
  181. // OnPacketSent Inputs the sent packet information into the sampler. Assumes that all
  182. // packets are sent in order. The information about the packet will not be
  183. // released from the sampler until it the packet is either acknowledged or
  184. // declared lost.
  185. func (s *BandwidthSampler) OnPacketSent(sentTime time.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) {
  186. s.lastSendPacket = lastSentPacket
  187. if !hasRetransmittableData {
  188. return
  189. }
  190. s.totalBytesSent += sentBytes
  191. // If there are no packets in flight, the time at which the new transmission
  192. // opens can be treated as the A_0 point for the purpose of bandwidth
  193. // sampling. This underestimates bandwidth to some extent, and produces some
  194. // artificially low samples for most packets in flight, but it provides with
  195. // samples at important points where we would not have them otherwise, most
  196. // importantly at the beginning of the connection.
  197. if bytesInFlight == 0 {
  198. s.lastAckedPacketAckTime = sentTime
  199. s.totalBytesSentAtLastAckedPacket = s.totalBytesSent
  200. // In this situation ack compression is not a concern, set send rate to
  201. // effectively infinite.
  202. s.lastAckedPacketSentTime = sentTime
  203. }
  204. s.connectionStats.Insert(lastSentPacket, sentTime, sentBytes, s)
  205. }
  206. // OnPacketAcked Notifies the sampler that the |lastAckedPacket| is acknowledged. Returns a
  207. // bandwidth sample. If no bandwidth sample is available,
  208. // QuicBandwidth::Zero() is returned.
  209. func (s *BandwidthSampler) OnPacketAcked(ackTime time.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample {
  210. sentPacketState := s.connectionStats.Get(lastAckedPacket)
  211. if sentPacketState == nil {
  212. return NewBandwidthSample()
  213. }
  214. sample := s.onPacketAckedInner(ackTime, lastAckedPacket, sentPacketState)
  215. s.connectionStats.Remove(lastAckedPacket)
  216. return sample
  217. }
  218. // onPacketAckedInner Handles the actual bandwidth calculations, whereas the outer method handles
  219. // retrieving and removing |sentPacket|.
  220. func (s *BandwidthSampler) onPacketAckedInner(ackTime time.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample {
  221. s.totalBytesAcked += sentPacket.size
  222. s.totalBytesSentAtLastAckedPacket = sentPacket.sendTimeState.totalBytesSent
  223. s.lastAckedPacketSentTime = sentPacket.sendTime
  224. s.lastAckedPacketAckTime = ackTime
  225. // Exit app-limited phase once a packet that was sent while the connection is
  226. // not app-limited is acknowledged.
  227. if s.isAppLimited && lastAckedPacket > s.endOfAppLimitedPhase {
  228. s.isAppLimited = false
  229. }
  230. // There might have been no packets acknowledged at the moment when the
  231. // current packet was sent. In that case, there is no bandwidth sample to
  232. // make.
  233. if sentPacket.lastAckedPacketSentTime.IsZero() {
  234. return NewBandwidthSample()
  235. }
  236. // Infinite rate indicates that the sampler is supposed to discard the
  237. // current send rate sample and use only the ack rate.
  238. sendRate := InfiniteBandwidth
  239. if sentPacket.sendTime.After(sentPacket.lastAckedPacketSentTime) {
  240. sendRate = BandwidthFromDelta(sentPacket.sendTimeState.totalBytesSent-sentPacket.totalBytesSentAtLastAckedPacket, sentPacket.sendTime.Sub(sentPacket.lastAckedPacketSentTime))
  241. }
  242. // During the slope calculation, ensure that ack time of the current packet is
  243. // always larger than the time of the previous packet, otherwise division by
  244. // zero or integer underflow can occur.
  245. if !ackTime.After(sentPacket.lastAckedPacketAckTime) {
  246. // TODO(wub): Compare this code count before and after fixing clock jitter
  247. // issue.
  248. // if sentPacket.lastAckedPacketAckTime.Equal(sentPacket.sendTime) {
  249. // This is the 1st packet after quiescense.
  250. // QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2);
  251. // } else {
  252. // QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 2, 2);
  253. // }
  254. return NewBandwidthSample()
  255. }
  256. ackRate := BandwidthFromDelta(s.totalBytesAcked-sentPacket.sendTimeState.totalBytesAcked,
  257. ackTime.Sub(sentPacket.lastAckedPacketAckTime))
  258. // Note: this sample does not account for delayed acknowledgement time. This
  259. // means that the RTT measurements here can be artificially high, especially
  260. // on low bandwidth connections.
  261. sample := &BandwidthSample{
  262. bandwidth: minBandwidth(sendRate, ackRate),
  263. rtt: ackTime.Sub(sentPacket.sendTime),
  264. }
  265. SentPacketToSendTimeState(sentPacket, &sample.stateAtSend)
  266. return sample
  267. }
  268. // OnPacketLost Informs the sampler that a packet is considered lost and it should no
  269. // longer keep track of it.
  270. func (s *BandwidthSampler) OnPacketLost(packetNumber congestion.PacketNumber) SendTimeState {
  271. ok, sentPacket := s.connectionStats.Remove(packetNumber)
  272. sendTimeState := SendTimeState{
  273. isValid: ok,
  274. }
  275. if sentPacket != nil {
  276. s.totalBytesLost += sentPacket.size
  277. SentPacketToSendTimeState(sentPacket, &sendTimeState)
  278. }
  279. return sendTimeState
  280. }
  281. // OnAppLimited Informs the sampler that the connection is currently app-limited, causing
  282. // the sampler to enter the app-limited phase. The phase will expire by
  283. // itself.
  284. func (s *BandwidthSampler) OnAppLimited() {
  285. s.isAppLimited = true
  286. s.endOfAppLimitedPhase = s.lastSendPacket
  287. }
  288. // SentPacketToSendTimeState Copy a subset of the (private) ConnectionStateOnSentPacket to the (public)
  289. // SendTimeState. Always set send_time_state->is_valid to true.
  290. func SentPacketToSendTimeState(sentPacket *ConnectionStateOnSentPacket, sendTimeState *SendTimeState) {
  291. sendTimeState.isAppLimited = sentPacket.sendTimeState.isAppLimited
  292. sendTimeState.totalBytesSent = sentPacket.sendTimeState.totalBytesSent
  293. sendTimeState.totalBytesAcked = sentPacket.sendTimeState.totalBytesAcked
  294. sendTimeState.totalBytesLost = sentPacket.sendTimeState.totalBytesLost
  295. sendTimeState.isValid = true
  296. }
  297. // ConnectionStates Record of the connection state at the point where each packet in flight was
  298. // sent, indexed by the packet number.
  299. // FIXME: using LinkedList replace map to fast remove all the packets lower than the specified packet number.
  300. type ConnectionStates struct {
  301. stats map[congestion.PacketNumber]*ConnectionStateOnSentPacket
  302. }
  303. func (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool {
  304. if _, ok := s.stats[packetNumber]; ok {
  305. return false
  306. }
  307. s.stats[packetNumber] = NewConnectionStateOnSentPacket(packetNumber, sentTime, bytes, sampler)
  308. return true
  309. }
  310. func (s *ConnectionStates) Get(packetNumber congestion.PacketNumber) *ConnectionStateOnSentPacket {
  311. return s.stats[packetNumber]
  312. }
  313. func (s *ConnectionStates) Remove(packetNumber congestion.PacketNumber) (bool, *ConnectionStateOnSentPacket) {
  314. state, ok := s.stats[packetNumber]
  315. if ok {
  316. delete(s.stats, packetNumber)
  317. }
  318. return ok, state
  319. }
  320. func NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket {
  321. return &ConnectionStateOnSentPacket{
  322. packetNumber: packetNumber,
  323. sendTime: sentTime,
  324. size: bytes,
  325. lastAckedPacketSentTime: sampler.lastAckedPacketSentTime,
  326. lastAckedPacketAckTime: sampler.lastAckedPacketAckTime,
  327. totalBytesSentAtLastAckedPacket: sampler.totalBytesSentAtLastAckedPacket,
  328. sendTimeState: SendTimeState{
  329. isValid: true,
  330. isAppLimited: sampler.isAppLimited,
  331. totalBytesSent: sampler.totalBytesSent,
  332. totalBytesAcked: sampler.totalBytesAcked,
  333. totalBytesLost: sampler.totalBytesLost,
  334. },
  335. }
  336. }