-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathprague_cc.cpp
More file actions
558 lines (507 loc) · 25.1 KB
/
Copy pathprague_cc.cpp
File metadata and controls
558 lines (507 loc) · 25.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
#include <chrono>
#include "prague_cc.h"
uint64_t mul_64_64_shift(uint64_t left, uint64_t right, uint32_t shift = 0)
{
uint64_t a0 = left & ((1ULL << 32)-1);
uint64_t a1 = left >> 32;
uint64_t b0 = right & ((1ULL << 32)-1);
uint64_t b1 = right >> 32;
uint64_t m0 = a0 * b0;
uint64_t m1 = a0 * b1;
uint64_t m2 = a1 * b0;
uint64_t m3 = a1 * b1;
uint64_t result_low;
uint64_t result_high;
m2 += (m0 >> 32);
m2 += m1;
/* Overflow */
if (m2 < m1)
m3 += (1ULL << 32);
result_low = (m0 & ((1ULL << 32)-1)) | (m2 << 32);
result_high = m3 + (m2 >> 32);
if (shift && 64 >= shift) {
result_low = (result_low >> shift) | (result_high << (64 - shift));
result_high = (result_high >> shift);
}
return (result_high) ? 0xffffffffffffffffULL : result_low;
}
uint64_t div_64_64_round(uint64_t a, uint64_t divisor)
{
uint64_t dividend = a + (divisor >> 1);
uint64_t overflow = (dividend < a) ? 1 : 0;
uint64_t quotient1 = 0;
uint64_t quotient2 = 0;
uint64_t quotient3 = 0;
uint64_t remainder = 0;
if (!divisor)
return 0xffffffffffffffffULL;
if (!overflow)
return dividend / divisor;
quotient1 = overflow / divisor;
/* Overflow */
if (quotient1)
return 0xffffffffffffffffULL;
remainder = overflow % divisor;
quotient2 = ((remainder << 32) | (dividend >> 32)) / divisor;
remainder = ((remainder << 32) | (dividend >> 32)) % divisor;
quotient3 = ((remainder << 32) | (dividend & 0xffffffff)) / divisor;
return (quotient2 << 32) + quotient3;
}
// Prague consts and methods
const count_tp MIN_STEP = 7; // Minimally wait for 7 RTTs to try to increase faster
const rate_tp RATE_SHIFT = 21; // per 1920kB/s = 15360kbps pacing rate wait one RTT longer, but leave a bit more slack: 2097152 B/s is >>21
const time_tp QUEUE_GROWTH = 1000; // target a queue growth of 1000us = 1ms after waiting pacing_rate >> RATE_SHIFT + MIN_STEP
const time_tp BURST_TIME = 250; // 250us
const time_tp REF_RTT = 25000; // 25ms
const uint8_t PROB_SHIFT = 20; // enough as max value that can control up to 100Gbps with r [Mbps] = 1/p - 1, p = 1/(r + 1) = 1/100001
const prob_tp MAX_PROB = 1 << PROB_SHIFT; // with r [Mbps] = 1/p - 1 = 2^20 Mbps = 1Tbps
const uint8_t ALPHA_SHIFT = 4; // >> 4 is divide by 16
const count_tp MIN_PKT_BURST = 1; // 1 packet
const count_tp MIN_PKT_WIN = 2; // 2 packets
const rate_tp MIN_PACKET_RATE = 80; // 2 packets per 25ms = the minimum packet rate to maintain by reducing the packet sizes
const uint8_t RATE_OFFSET = 3; // +3% and -3% for non-RTmode transfer during 1st and 2nd halve vrtt
const count_tp MIN_FRAME_WIN = 2; // 2 frames
time_tp PragueCC::Now() // Returns number of µs since first call
{
// Checks if now==0; skip this value used to check uninitialized timepstamp
if (m_start_ref == 0) {
m_start_ref = time_tp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
if (m_start_ref == 0) {
m_start_ref = -1; // init m_start_ref with -1 to avoid next now to be less than this value
}
return 1; // make sure we don't return less than or equal to 0
}
time_tp now = time_tp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()) - m_start_ref;
if (now == 0) {
return 1; // make sure we don't return 0
}
return now;
}
time_tp PragueCC::get_ref_rtt()
{
if (m_frame_interval)
return m_frame_interval;
else
return REF_RTT;
}
count_tp PragueCC::get_alpha_shift()
{
if (m_frame_interval)
return (1 << ALPHA_SHIFT) * (REF_RTT) / (m_frame_interval);
else
return 1 << ALPHA_SHIFT;
}
PragueCC::PragueCC(
size_tp max_packet_size,
fps_tp fps,
time_tp frame_budget,
rate_tp init_rate,
count_tp init_window,
rate_tp min_rate,
rate_tp max_rate)
{
m_start_ref = 0;
time_tp ts_now = Now();
// parameters
m_init_rate = init_rate;
m_init_window = window_tp(init_window) * max_packet_size * 1000000;
m_min_rate = min_rate;
m_max_rate = max_rate;
m_max_packet_size = max_packet_size;
m_frame_interval = fps ? 1000000 / fps : 0;
m_frame_budget = frame_budget;
if (m_frame_budget > m_frame_interval)
m_frame_budget = m_frame_interval;
// both end variables
m_ts_remote = 0; // to keep the frozen timestamp from the peer, and echo it back defrosted
m_rtt = 0; // last reported rtt (only for stats)
m_srtt = 0; // our own measured and smoothed RTT (smoothing factor = 1/8)
m_vrtt = 0; // our own virtual RTT = max(srtt, 25ms)
// receiver end variables (to be echoed to sender)
m_r_prev_ts = 0; // used to see if an ack isn't older than the previous ack
m_r_packets_received = 0; // as a receiver, keep counters to echo back
m_r_packets_CE = 0;
m_r_packets_lost = 0;
m_r_error_L4S = false; // as a receiver, check L4S-ECN validity to echo back an error
// sender end variables
m_cc_ts = ts_now; // time of last cc update
m_packets_received = 0; // latest known receiver end counters
m_packets_CE = 0;
m_packets_lost = 0;
m_packets_sent = 0;
m_error_L4S = false; // latest known receiver end error state
// for alpha calculation, keep the previous alpha variables' state
m_alpha_ts = ts_now; // start recording alpha from now on (every vrtt)
m_alpha_packets_received = 0;
m_alpha_packets_CE = 0;
m_alpha_packets_lost = 0;
m_alpha_packets_sent = 0;
// for loss and recovery calculation
m_loss_ts = 0;
m_loss_cca = cca_prague_win;
m_lost_window = 0;
m_lost_rate = 0;
m_loss_packets_lost = 0;
m_loss_packets_sent = 0;
m_lost_rtts_to_growth = 0;
// for congestion experienced and window reduction (cwr) calculation
m_cwr_ts = 0;
m_cwr_packets_sent = 0;
// state updated for the actual congestion control variables
m_cc_state = cs_init;
m_cca_mode = cca_prague_win;
m_rtts_to_growth= (init_rate >> RATE_SHIFT) + MIN_STEP; // virtual rtts before going into growth mode
m_alpha = 0;
m_pacing_rate = init_rate;
m_fractional_window = m_init_window;
m_packet_size = m_pacing_rate * get_ref_rtt() / 1000000 / MIN_PKT_WIN; // B/p = B/s * 25ms/burst / 2p/window
if (m_packet_size < PRAGUE_MINMTU)
m_packet_size = PRAGUE_MINMTU;
if (m_packet_size > m_max_packet_size)
m_packet_size = m_max_packet_size;
m_packet_burst = count_tp(m_pacing_rate * BURST_TIME / 1000000 / m_packet_size); // p = B/s * 250µs / B/p
if (m_packet_burst < MIN_PKT_BURST) {
m_packet_burst = MIN_PKT_BURST;
}
m_packet_window = count_tp((m_fractional_window / 1000000 + m_packet_size - 1) / m_packet_size);
if (m_packet_window < MIN_PKT_WIN) {
m_packet_window = MIN_PKT_WIN;
}
}
PragueCC::~PragueCC()
{}
bool PragueCC::RFC8888Received(size_t num_rtt, time_tp *pkts_rtt)
{
for (size_t i = 0; i < num_rtt; i++) {
m_rtt = pkts_rtt[i];
if (m_cc_state != cs_init)
m_srtt += (m_rtt - m_srtt) >> 3;
else
m_srtt = m_rtt;
m_vrtt = (m_srtt > get_ref_rtt()) ? m_srtt : get_ref_rtt();
}
return true;
}
bool PragueCC::PacketReceived( // call this when a packet is received from peer. Returns true if this is a newer packet, false if this is an older
const time_tp timestamp, // timestamp from peer, freeze and keep this time
const time_tp echoed_timestamp) // echoed_timestamp can be used to calculate the RTT
{
// Ignore older or invalid ACKs (these counters can't go down in new ACKs)
if ((m_cc_state != cs_init) && (m_r_prev_ts - timestamp > 0)) // is this an older timestamp?
return false;
time_tp ts = Now();
m_ts_remote = ts - timestamp; // freeze the remote timestamp
m_rtt = ts - echoed_timestamp; // calculate the new rtt sample
if (m_cc_state != cs_init)
m_srtt += (m_rtt - m_srtt) >> 3; // smooth with EWMA of 1/8th
else
m_srtt = m_rtt;
m_vrtt = (m_srtt > get_ref_rtt()) ? m_srtt : get_ref_rtt(); // calculate the virtual RTT (if srtt < 25ms reference RTT)
m_r_prev_ts = timestamp;
return true;
}
bool PragueCC::ACKReceived( // call this when an ACK is received from peer. Returns true if this is a newer ACK, false if this is an old ACK
count_tp packets_received, // echoed_packet counter
count_tp packets_CE, // echoed CE counter
count_tp packets_lost, // echoed lost counter
count_tp packets_sent, // local counter of packets sent up to now, an RTT is reached if remote ACK packets_received+packets_lost
bool error_L4S, // receiver found a bleached/error ECN; stop using L4S_id on the sending packets!
count_tp &inflight) // how many packets are in flight after the ACKed
{
// Ignore older or invalid ACKs (these counters can't go down in new ACKs)
if ((m_packets_received - packets_received > 0) || (m_packets_CE - packets_CE > 0))
return false;
// select the rate- or window-based update, but keep the rate stable on switching
time_tp pacing_interval = m_packet_size * 1000000 / m_pacing_rate; // calculate the max expected rtt from pacing
//printf("FrW: %ld, SRTT: %d, Pacing interval: %ld, packet_size: %ld, packet_burst: %d, pacing_rate: %ld\n", m_fractional_window, m_srtt, m_packet_size * 1000000 * m_packet_burst / m_pacing_rate, m_packet_size, m_packet_burst, m_pacing_rate);
time_tp srtt = (m_srtt);
// initialize the window with the initial pacing rate
if (m_cc_state == cs_init)
{
m_fractional_window = srtt * m_pacing_rate;
m_cc_state = cs_cong_avoid;
}
// select the rate- or window-based update, but keep the rate stable on switching
// below the pacing interval or 2ms the RTT is too unstable to calculate a rate. Also no queue can be identified reliably.
// but only use rate-based update if we saw resent marks or drops
if (m_rtts_to_growth && ((srtt <= 2000) || (srtt <= pacing_interval))) {
// keep rate stable when large dip in srtt
m_cca_mode = cca_prague_rate;
}
else {
// keep rate stable when large jump in srtt
if (m_cca_mode == cca_prague_rate)
m_fractional_window = srtt * m_pacing_rate;
m_cca_mode = cca_prague_win;
}
time_tp ts = Now();
// Update alpha if both a window and a virtual rtt are passed
if ((packets_received + packets_lost - m_alpha_packets_sent > 0) && (ts - m_alpha_ts - m_vrtt >= 0)) {
//if ((packets_received - m_alpha_packets_received + packets_lost - m_alpha_packets_lost > max(2, m_fractional_window / m_packet_size / 1000000))
// && (now() - m_prev_cycle > 25000)) {
// prob_tp prob = (packets_CE - m_alpha_packets_CE) << PROB_SHIFT / (packets_received - m_alpha_packets_received);
prob_tp prob = (prob_tp(packets_CE - m_alpha_packets_CE) << PROB_SHIFT) / (packets_received - m_alpha_packets_received);
m_alpha += ((prob - m_alpha) / get_alpha_shift());
m_alpha = (m_alpha > MAX_PROB) ? MAX_PROB : m_alpha;
m_alpha_packets_sent = packets_sent;
m_alpha_packets_CE = packets_CE;
m_alpha_packets_received = packets_received;
m_alpha_ts = ts;
// also reduce the rtts to growth if not already 0
if (m_rtts_to_growth > 0)
m_rtts_to_growth--;
}
// Undo the window reduction if the lost count is again down to the one that caused a reduction (reordered iso loss)
if ((m_lost_window > 0 || m_lost_rate > 0) && (m_loss_packets_lost - packets_lost >= 0)) {
m_cca_mode = m_loss_cca; // restore the cca mode before recovery
if (m_cca_mode == cca_prague_rate) {
m_pacing_rate += m_lost_rate; // add the reduction to the rate again
m_lost_rate = 0; // can be done only once
} else {
m_fractional_window += m_lost_window; // add the reduction to the window again
m_lost_window = 0; // can be done only once
}
m_rtts_to_growth -= m_lost_rtts_to_growth; // restore the rtts to growth
if (m_rtts_to_growth < 0)
m_rtts_to_growth = 0;
m_lost_rtts_to_growth = 0; // clear all lost growth rtts
m_cc_state = cs_cong_avoid; // restore the loss state
}
// Clear the in_loss state if in_loss and a real and virtual rtt are passed
if ((m_cc_state == cs_in_loss) && (packets_received + packets_lost - m_loss_packets_sent > 0) && (ts - m_loss_ts - m_vrtt >= 0)) {
m_cc_state = cs_cong_avoid; // set the loss state to avoid multiple reductions per RTT
// keep all loss info for undo if later reordering is found (loss is reduced to m_loss_packets_lost again)
}
// Reduce the window if the loss count is increased
if ((m_cc_state != cs_in_loss) && (m_packets_lost - packets_lost < 0)) {
// vRTTs needed to get to the time where a REF_RTT flow would hit the same bottleneck again. after that do 1ms growth
count_tp rtts_to_growth = m_pacing_rate / 2 / m_max_packet_size * REF_RTT / m_vrtt * REF_RTT / 1000000; // rescale twice
// first reset the growth waiting time, but prepare to undo
m_lost_rtts_to_growth += rtts_to_growth - m_rtts_to_growth; // accumulate over different reordering rtts if applicable
if (m_lost_rtts_to_growth > rtts_to_growth)
m_lost_rtts_to_growth = rtts_to_growth; // no need to undo more than what will be used next
m_rtts_to_growth = rtts_to_growth; // also equivalent to m_rtts_to_growth += m_lost_rtts_to_growth; so can be undone with -=
if (m_cca_mode == cca_prague_win) {
m_lost_window = m_fractional_window / 2; // remember the reduction
m_fractional_window -= m_lost_window; // reduce the window
} else { // (m_cca_mode == cca_prague_rate)
m_lost_rate = m_pacing_rate / 2; // remember the reduction
m_pacing_rate -= m_lost_rate; // reduce the rate
}
m_cc_state = cs_in_loss; // set the loss state to avoid multiple reductions per RTT
m_loss_cca = m_cca_mode;
m_loss_packets_sent = packets_sent; // set when to end in_loss state
m_loss_ts = ts; // set the loss timestampt to check if a virtRtt is passed
m_loss_packets_lost = m_packets_lost; // remember the previous packets_lost for the undo if needed
}
// Increase the window if not in-loss for all the non-CE ACKs
count_tp acks = (packets_received - m_packets_received) - (packets_CE - m_packets_CE);
if ((m_cc_state != cs_in_loss) && (acks > 0))
{
size_tp increment = mul_64_64_shift(m_pacing_rate, QUEUE_GROWTH) / 1000000; // incr = B/s * 1ms
if ((increment < m_max_packet_size) || m_rtts_to_growth) // increment with 1ms queue delay if no more rtts to wait for growth and if > than 1 max packet
increment = m_max_packet_size;
// W[p] = W + acks / W * (srrt/vrtt)², but in the right order to not lose precision
// W[µB] = W + acks * mtu² * 1000000² / W * (srrt/vrtt)²
// correct order to prevent loss of precision
if (m_cca_mode == cca_prague_win) {
uint64_t divisor = mul_64_64_shift(m_vrtt, m_vrtt); // Use mul_64_64 to implicitely convert to uint64_t
uint64_t scaler = div_64_64_round((uint64_t) srtt * 1000000 * srtt, divisor);
//uint64_t scaler = ((uint64_t) srtt * 1000000 * srtt + (divisor >> 1)) / divisor;
uint64_t increase = div_64_64_round(acks * m_packet_size * scaler * 1000000, m_fractional_window);
//uint64_t increase = (acks * m_packet_size * scaler * 1000000 + (m_fractional_window >> 1)) / m_fractional_window;
uint64_t scaled_increase = mul_64_64_shift(increase, increment);
m_fractional_window += scaled_increase;
//m_fractional_window += acks * (uint64_t) m_packet_size * srtt * 1000000 / m_vrtt * (uint64_t) increment * srtt / m_vrtt * 1000000 / m_fractional_window;
} else {
uint64_t divisor = mul_64_64_shift(m_packet_size, 1000000);
uint64_t invscaler = div_64_64_round(mul_64_64_shift(m_pacing_rate, m_vrtt), divisor);
//uint64_t invscaler = (mul_64_64_shift(m_pacing_rate, m_vrtt) + (divisor >> 1)) / divisor;
uint64_t increase = div_64_64_round(mul_64_64_shift((uint64_t) acks * increment, 1000000), m_vrtt);
//uint64_t increase = ((uint64_t) acks * m_packet_size * 1000000 + (m_vrtt >> 1)) / m_vrtt;
uint64_t scaled_increase = div_64_64_round(increase, invscaler);
//uint64_t scaled_increase = (increase + (invscaler >> 1)) / invscaler;
m_pacing_rate += scaled_increase;
//m_pacing_rate += acks * increment * 1000000 / m_vrtt * m_packet_size / m_vrtt * 1000000 / m_pacing_rate;
}
}
// Clear the in_cwr state if in_cwr and a real and vrtual rtt are passed
if ((m_cc_state == cs_in_cwr) && (packets_received + packets_lost - m_cwr_packets_sent > 0) && (ts - m_cwr_ts - m_vrtt >= 0)) {
m_cc_state = cs_cong_avoid; // set the loss state to avoid multiple reductions per RTT
}
// Reduce the window if the CE count is increased, and if not in-loss and not in-cwr
if ((m_cc_state == cs_cong_avoid) && (m_packets_CE - packets_CE < 0)) {
m_rtts_to_growth = (m_pacing_rate >> RATE_SHIFT) + MIN_STEP; // first reset the growth waiting time
if (m_cca_mode == cca_prague_win) {
m_fractional_window -= m_fractional_window * m_alpha >> (PROB_SHIFT + 1); // reduce the window by a factor alpha/2
} else {
m_pacing_rate -= m_pacing_rate * m_alpha >> (PROB_SHIFT + 1); // reduce the rate by a factor alpha/2
}
m_cc_state = cs_in_cwr; // set the loss state to avoid multiple reductions per RTT
m_cwr_packets_sent = packets_sent; // set when to end in_loss state
m_cwr_ts = ts; // set the cwr timestampt to check if a virtRtt is passed
}
// Updating dependant parameters
// align and limit pacing rate and fractional window
if (m_cca_mode != cca_prague_rate)
m_pacing_rate = m_fractional_window / srtt; // in B/s
if (m_pacing_rate < m_min_rate)
{
m_pacing_rate = m_min_rate;
m_fractional_window = m_pacing_rate * srtt; // in uB; make sure it is updated
}
if (m_pacing_rate > m_max_rate)
{
m_pacing_rate = m_max_rate;
m_fractional_window = m_pacing_rate * srtt; // in uB; make sure it is updated
}
if (m_cca_mode == cca_prague_rate)
m_fractional_window = m_pacing_rate * srtt; // in uB; only update if in rate mode, otherwise loss of precision
if (m_fractional_window == 0)
m_fractional_window = 1;
//determine packet size
// m_packet_size = m_pacing_rate * m_vrtt / 1000000 / MIN_PKT_WIN; // B/p = B/s * 25ms/burst / 2p/burst
m_packet_size = m_pacing_rate / MIN_PACKET_RATE;
if (m_packet_size < PRAGUE_MINMTU)
m_packet_size = PRAGUE_MINMTU;
if (m_packet_size > m_max_packet_size)
m_packet_size = m_max_packet_size;
// packet burst
m_packet_burst = count_tp(m_pacing_rate * BURST_TIME / 1000000 / m_packet_size); // p = B/s * 250µs / B/p
if (m_packet_burst < MIN_PKT_BURST) {
m_packet_burst = MIN_PKT_BURST;
}
// packet window: allow 3% higher pacing rate and round up (add one). Window should not block pacing; block only when the network has a freeze or hickup.
m_packet_window = count_tp((m_fractional_window * (100 + RATE_OFFSET) / 100000000) / m_packet_size + 1);
if (m_packet_window < MIN_PKT_WIN) {
m_packet_window = MIN_PKT_WIN;
}
// remember this previous ACK for the next ACK
m_cc_ts = ts;
m_packets_received = packets_received; // can NOT go down
m_packets_CE = packets_CE; // can NOT go down
m_packets_lost = packets_lost; // CAN go down
m_packets_sent = packets_sent; // can NOT go down
if (error_L4S) m_error_L4S = true; // can NOT reset
inflight = packets_sent - m_packets_received - m_packets_lost;
return true;
}
// Can this be combined with the normal ACKReceived?
/*bool PragueCC::FrameACKReceived( // call this when a frame ACK is received from peer
count_tp packets_received, // echoed_packet counter
count_tp packets_CE, // echoed CE counter
count_tp packets_lost, // echoed lost counter
bool error_L4S) // receiver found a bleached/error ECN; stop using L4S_id on the sending packets!
{
return true;
}*/
void PragueCC::DataReceivedSequence( // call this every time when a data packet is received as a receiver
ecn_tp ip_ecn, // IP.ECN field value
count_tp packet_seq_nr) // sequence number of the received packet
{
ip_ecn = ecn_tp(ip_ecn & ecn_ce);
m_r_packets_received++; // assuming no duplicates (by for instance the NW)
count_tp skipped = packet_seq_nr - m_r_packets_received - m_r_packets_lost;
if (skipped >= 0)
m_r_packets_lost += skipped; // 0 or more lost
else if (m_r_packets_lost > 0)
m_r_packets_lost--; // reordered packet
if (ip_ecn == ecn_ce)
{
m_r_packets_CE++;
}
else if (ip_ecn != ecn_l4s_id)
{
m_r_error_L4S = true;
}
}
void PragueCC::DataReceived( // call this when a data packet is received as a receiver and you can identify lost packets
ecn_tp ip_ecn, // IP.ECN field value
count_tp packets_lost) // packets skipped; can be optionally -1 to potentially undo a previous cwindow reduction
{
ip_ecn = ecn_tp(ip_ecn & ecn_ce);
m_r_packets_received++;
m_r_packets_lost += packets_lost;
if (ip_ecn == ecn_ce)
{
m_r_packets_CE++;
}
else if (ip_ecn != ecn_l4s_id)
{
m_r_error_L4S = true;
}
}
void PragueCC::ResetCCInfo() // call this when there is a RTO detected
{
m_cc_ts = Now();
m_cc_state = cs_init;
m_cca_mode = cca_prague_win;
m_alpha_ts = m_cc_ts;
m_alpha = 0;
m_pacing_rate = m_init_rate;
m_fractional_window = m_max_packet_size * 1000000; // reset to 1 packet
m_packet_burst = MIN_PKT_BURST;
m_packet_size = m_max_packet_size;
m_packet_window = MIN_PKT_WIN;
m_rtts_to_growth = (m_pacing_rate >> RATE_SHIFT) + MIN_STEP; // virtual rtts before going into growth mode
m_lost_rtts_to_growth = 0;
}
void PragueCC::GetTimeInfo( // when the any-app needs to send a packet
time_tp ×tamp, // Own timestamp to echo by peer
time_tp &echoed_timestamp, // defrosted timestamp echoed to peer
ecn_tp &ip_ecn)
{
timestamp = Now();
if (m_ts_remote)
echoed_timestamp = timestamp - m_ts_remote; // if frozen
else
echoed_timestamp = 0;
//echoed_timestamp = m_ts_remote; // if not frozen
if (m_error_L4S == true)
{
ip_ecn = ecn_not_ect;
} else {
ip_ecn = ecn_l4s_id;
}
}
void PragueCC::GetCCInfo( // when the sending-app needs to send a packet
rate_tp &pacing_rate, // rate to pace the packets
count_tp &packet_window, // the congestion window in number of packets
count_tp &packet_burst, // number of packets that can be paced at once (<250µs)
size_tp &packet_size) // the packet size to transmit
{
if (Now() - m_alpha_ts - (m_vrtt >> 1) >= 0)
pacing_rate = m_pacing_rate * 100 / (100 + RATE_OFFSET);
else
pacing_rate = m_pacing_rate * (100 + RATE_OFFSET) / 100;
packet_window = m_packet_window;
packet_burst = m_packet_burst;
packet_size = m_packet_size;
}
void PragueCC::GetCCInfoVideo( // when the sending app needs to send a frame
rate_tp &pacing_rate, // rate to pace the packets
size_tp &frame_size, // the size of a single frame in Bytes
count_tp &frame_window, // the congestion window in number of frames
count_tp &packet_burst, // number of packets that can be paced at once (<250µs)
size_tp &packet_size) // the packet size to transmit
{
pacing_rate = m_pacing_rate;
packet_burst = m_packet_burst;
packet_size = m_packet_size;
frame_size = (m_packet_size > m_pacing_rate * m_frame_budget / 1000000) ? (m_packet_size) : (m_pacing_rate * m_frame_budget / 1000000);
frame_window = m_packet_window * m_packet_size / frame_size;
if (frame_window < MIN_FRAME_WIN) {
frame_window = MIN_FRAME_WIN;
}
}
void PragueCC::GetACKInfo( // when the receiving-app needs to send a packet
count_tp &packets_received, // packet counter to echo
count_tp &packets_CE, // CE counter to echo
count_tp &packets_lost, // lost counter to echo (if used)
bool &error_L4S) // bleached/error ECN status to echo
{
packets_received = m_r_packets_received;
packets_CE = m_r_packets_CE;
packets_lost = m_r_packets_lost;
error_L4S = m_r_error_L4S;
}