quiche/recovery/congestion/
delivery_rate.rs1use std::time::Duration;
33use std::time::Instant;
34
35use super::Acked;
36use super::Sent;
37
38#[derive(Debug)]
39pub struct Rate {
40 delivered: usize,
41
42 delivered_time: Instant,
43
44 first_sent_time: Instant,
45
46 end_of_app_limited: u64,
48
49 last_sent_packet: u64,
51
52 largest_acked: u64,
54
55 rate_sample: RateSample,
57}
58
59impl Default for Rate {
60 fn default() -> Self {
61 let now = Instant::now();
62
63 Rate {
64 delivered: 0,
65
66 delivered_time: now,
67
68 first_sent_time: now,
69
70 end_of_app_limited: 0,
71
72 last_sent_packet: 0,
73
74 largest_acked: 0,
75
76 rate_sample: RateSample::default(),
77 }
78 }
79}
80
81impl Rate {
82 pub fn on_packet_sent(
83 &mut self, pkt: &mut Sent, bytes_in_flight: usize, bytes_lost: u64,
84 ) {
85 if bytes_in_flight == 0 {
87 self.first_sent_time = pkt.time_sent;
88 self.delivered_time = pkt.time_sent;
89 }
90
91 pkt.first_sent_time = self.first_sent_time;
92 pkt.delivered_time = self.delivered_time;
93 pkt.delivered = self.delivered;
94 pkt.is_app_limited = self.app_limited();
95 pkt.tx_in_flight = bytes_in_flight;
96 pkt.lost = bytes_lost;
97
98 self.last_sent_packet = pkt.pkt_num;
99 }
100
101 pub fn update_rate_sample(&mut self, pkt: &Acked, now: Instant) {
103 self.delivered += pkt.size;
104 self.delivered_time = now;
105
106 if self.rate_sample.prior_time.is_none() ||
109 pkt.delivered > self.rate_sample.prior_delivered
110 {
111 self.rate_sample.prior_delivered = pkt.delivered;
112 self.rate_sample.prior_time = Some(pkt.delivered_time);
113 self.rate_sample.is_app_limited = pkt.is_app_limited;
114 self.rate_sample.send_elapsed =
115 pkt.time_sent.saturating_duration_since(pkt.first_sent_time);
116 self.rate_sample.rtt = pkt.rtt;
117 self.rate_sample.ack_elapsed = self
118 .delivered_time
119 .saturating_duration_since(pkt.delivered_time);
120
121 self.first_sent_time = pkt.time_sent;
122 }
123
124 self.largest_acked = self.largest_acked.max(pkt.pkt_num);
125 }
126
127 pub fn generate_rate_sample(&mut self, min_rtt: Duration) {
128 if self.app_limited() && self.largest_acked > self.end_of_app_limited {
130 self.update_app_limited(false);
131 }
132
133 if self.rate_sample.prior_time.is_some() {
134 let interval = self
135 .rate_sample
136 .send_elapsed
137 .max(self.rate_sample.ack_elapsed);
138
139 self.rate_sample.delivered =
140 self.delivered - self.rate_sample.prior_delivered;
141 self.rate_sample.interval = interval;
142
143 if interval < min_rtt {
144 self.rate_sample.interval = Duration::ZERO;
145
146 return;
148 }
149
150 if !interval.is_zero() {
151 self.rate_sample.delivery_rate =
153 (self.rate_sample.delivered as f64 / interval.as_secs_f64())
154 as u64;
155 }
156 }
157 }
158
159 pub fn update_app_limited(&mut self, v: bool) {
160 self.end_of_app_limited = if v { self.last_sent_packet.max(1) } else { 0 }
161 }
162
163 pub fn app_limited(&mut self) -> bool {
164 self.end_of_app_limited != 0
165 }
166
167 pub fn delivered(&self) -> usize {
168 self.delivered
169 }
170
171 pub fn sample_delivery_rate(&self) -> u64 {
172 self.rate_sample.delivery_rate
173 }
174
175 pub fn sample_rtt(&self) -> Duration {
176 self.rate_sample.rtt
177 }
178
179 pub fn sample_is_app_limited(&self) -> bool {
180 self.rate_sample.is_app_limited
181 }
182
183 pub fn sample_delivered(&self) -> usize {
184 self.rate_sample.delivered
185 }
186
187 pub fn sample_prior_delivered(&self) -> usize {
188 self.rate_sample.prior_delivered
189 }
190}
191
192#[derive(Default, Debug)]
193struct RateSample {
194 delivery_rate: u64,
195
196 is_app_limited: bool,
197
198 interval: Duration,
199
200 delivered: usize,
201
202 prior_delivered: usize,
203
204 prior_time: Option<Instant>,
205
206 send_elapsed: Duration,
207
208 ack_elapsed: Duration,
209
210 rtt: Duration,
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 use crate::packet;
218 use crate::ranges;
219 use crate::recovery::congestion::recovery::LegacyRecovery;
220 use crate::recovery::HandshakeStatus;
221 use crate::recovery::RecoveryOps;
222 use crate::Config;
223
224 use smallvec::smallvec;
225
226 #[test]
227 fn rate_check() {
228 let config = Config::new(0xbabababa).unwrap();
229 let mut r = LegacyRecovery::new(&config);
230
231 let now = Instant::now();
232 let mss = r.max_datagram_size();
233
234 for pn in 0..2 {
236 let pkt = Sent {
237 pkt_num: pn,
238 frames: smallvec![],
239 time_sent: now,
240 time_acked: None,
241 time_lost: None,
242 size: mss,
243 ack_eliciting: true,
244 in_flight: true,
245 delivered: 0,
246 delivered_time: now,
247 first_sent_time: now,
248 is_app_limited: false,
249 has_data: false,
250 tx_in_flight: 0,
251 lost: 0,
252 pmtud: false,
253 };
254
255 r.on_packet_sent(
256 pkt,
257 packet::Epoch::Application,
258 HandshakeStatus::default(),
259 now,
260 "",
261 );
262 }
263
264 let rtt = Duration::from_millis(50);
265 let now = now + rtt;
266
267 for pn in 0..2 {
269 let acked = Acked {
270 pkt_num: pn,
271 time_sent: now,
272 size: mss,
273 rtt,
274 delivered: 0,
275 delivered_time: now,
276 first_sent_time: now.checked_sub(rtt).unwrap(),
277 is_app_limited: false,
278 };
279
280 r.congestion.delivery_rate.update_rate_sample(&acked, now);
281 }
282
283 r.congestion.delivery_rate.generate_rate_sample(rtt);
285
286 assert_eq!(r.congestion.delivery_rate.delivered(), 2400);
288
289 assert_eq!(r.delivery_rate(), 48000);
291 }
292
293 #[test]
294 fn app_limited_cwnd_full() {
295 let config = Config::new(0xbabababa).unwrap();
296 let mut r = LegacyRecovery::new(&config);
297
298 let now = Instant::now();
299 let mss = r.max_datagram_size();
300
301 for pn in 0..10 {
303 let pkt = Sent {
304 pkt_num: pn,
305 frames: smallvec![],
306 time_sent: now,
307 time_acked: None,
308 time_lost: None,
309 size: mss,
310 ack_eliciting: true,
311 in_flight: true,
312 delivered: 0,
313 delivered_time: now,
314 first_sent_time: now,
315 is_app_limited: false,
316 has_data: false,
317 tx_in_flight: 0,
318 lost: 0,
319 pmtud: false,
320 };
321
322 r.on_packet_sent(
323 pkt,
324 packet::Epoch::Application,
325 HandshakeStatus::default(),
326 now,
327 "",
328 );
329 }
330
331 assert!(!r.app_limited());
332 assert!(!r.congestion.delivery_rate.sample_is_app_limited());
333 }
334
335 #[test]
336 fn app_limited_check() {
337 let config = Config::new(0xbabababa).unwrap();
338 let mut r = LegacyRecovery::new(&config);
339
340 let now = Instant::now();
341 let mss = r.max_datagram_size();
342
343 for pn in 0..5 {
345 let pkt = Sent {
346 pkt_num: pn,
347 frames: smallvec![],
348 time_sent: now,
349 time_acked: None,
350 time_lost: None,
351 size: mss,
352 ack_eliciting: true,
353 in_flight: true,
354 delivered: 0,
355 delivered_time: now,
356 first_sent_time: now,
357 is_app_limited: false,
358 has_data: false,
359 tx_in_flight: 0,
360 lost: 0,
361 pmtud: false,
362 };
363
364 r.on_packet_sent(
365 pkt,
366 packet::Epoch::Application,
367 HandshakeStatus::default(),
368 now,
369 "",
370 );
371 }
372
373 let rtt = Duration::from_millis(50);
374 let now = now + rtt;
375
376 let mut acked = ranges::RangeSet::default();
377 acked.insert(0..5);
378
379 assert_eq!(
380 r.on_ack_received(
381 &acked,
382 25,
383 packet::Epoch::Application,
384 HandshakeStatus::default(),
385 now,
386 "",
387 ),
388 (0, 0, mss * 5),
389 );
390
391 assert!(r.app_limited());
392 assert!(!r.congestion.delivery_rate.sample_is_app_limited());
394 assert_eq!(r.congestion.delivery_rate.sample_rtt(), rtt);
395 }
396}