Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pacing #452

Merged
merged 39 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0e324ca
implement pacing
kazuho May 17, 2021
5dffd9d
[cli] unify `-w` to `-C`, adding support for pacing
kazuho May 17, 2021
d0a6996
Merge branch 'master' into kazuho/pacer
kazuho May 17, 2021
a7d0d26
oops, add missing
kazuho May 17, 2021
a290c86
rename
kazuho May 17, 2021
6f32dd7
fix doc
kazuho May 17, 2021
fb2517c
add comment
kazuho May 17, 2021
261d6a9
remove unneeded
kazuho May 17, 2021
c9be530
when the flow rate is less than 10mtu / msec, make sure to not send a…
kazuho May 17, 2021
2879743
fix underflow; no need to cap at 1mtu when the unit is in bytes and q…
kazuho May 17, 2021
dc13a40
let CC specify the pacing multiplier
kazuho May 18, 2021
e974f49
burst_credit is non-negative
kazuho May 18, 2021
32b6ebb
add fast test
kazuho May 18, 2021
95f6f52
merge branch 'master' into kazuho/pacer
kazuho Oct 20, 2023
2ccbb25
Merge branch 'master' into kazuho/pacer
kazuho Nov 4, 2023
adb3079
compare with the token
kazuho Nov 4, 2023
597011c
add pacer support to pico
kazuho Nov 6, 2023
54329ce
s/cwnd_limited/cc_limited/g now that we have pacer
kazuho Nov 11, 2023
c8ec914
calculate delivery rate when pacer-limited
kazuho Nov 11, 2023
4d486d0
refactor, adding probes to mark enter to / exit of cc-limited state
kazuho Nov 11, 2023
b8a2332
Merge branch 'master' into kazuho/pacer
kazuho Nov 11, 2023
a237da9
update test code as well
kazuho Nov 11, 2023
dc9905b
[reno][pico] do not increase CWND when the connection is in app-limit…
kazuho Nov 13, 2023
f5ac5bc
Merge branch 'master' into kazuho/pacer
kazuho Mar 4, 2024
4e9b822
hard code pacer multiplier to 2x, moving the logic of selecting the m…
kazuho Mar 4, 2024
0899a4b
we do not use the macro
kazuho Mar 5, 2024
c9452ab
talk about latest rtt too
kazuho Mar 5, 2024
6adad7f
Merge branch 'master' into kazuho/pacer
kazuho Mar 5, 2024
fd91dbc
Merge branch 'master' into kazuho/pacer
kazuho Mar 6, 2024
2444845
oops; CWND never increases (regress of dc9905b)
kazuho Mar 7, 2024
cde56ac
add option to turn of app-limited detection
kazuho Mar 7, 2024
a7ae2a3
add tests that confirm exponential growth during slow start
kazuho Mar 7, 2024
d10fa72
split test to app-limited and not
kazuho Mar 8, 2024
6b21696
assume the connection is never CC-limited until address validation is…
kazuho Mar 8, 2024
73fd022
split the pacing side
kazuho Mar 8, 2024
fc7e0bd
[pacer] spawn 2 full-sized packets at once
kazuho Mar 9, 2024
9275ad4
Merge branch 'master' into kazuho/pacer
kazuho Mar 9, 2024
58f8f36
clang-format
kazuho Mar 9, 2024
b36dc96
rename
kazuho Mar 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ SET(UNITTEST_SOURCE_FILES
t/loss.c
t/lossy.c
t/maxsender.c
t/pacer.c
t/ranges.c
t/rate.c
t/remote_cid.c
Expand Down
4 changes: 4 additions & 0 deletions include/quicly.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ struct st_quicly_context_t {
* whether to use ECN on the send side; ECN is always on on the receive side
*/
unsigned enable_ecn : 1;
/**
* if pacing should be used
*/
unsigned use_pacing : 1;
/**
*
*/
Expand Down
4 changes: 4 additions & 0 deletions include/quicly/cc.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ typedef struct st_quicly_cc_t {
* Packet number indicating end of recovery period, if in recovery.
*/
uint64_t recovery_end;
/**
* multiplier to be applied for pacing, in 1/16 (e.g., 32 to send at twice CWND/RTT)
*/
uint32_t pacer_multiplier;
/**
* If the most recent loss episode was signalled by ECN only (i.e., no packet loss).
*/
Expand Down
149 changes: 149 additions & 0 deletions include/quicly/pacer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (c) 2021 Fastly, Kazuho Oku
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef quicly_pacer_h
#define quicly_pacer_h

#include <assert.h>
#include <stddef.h>
#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* Simple pacer. The design guarantees that the formula below is met for any given pacer-restricted period:
*
* flow_rate * duration + burst_credit <= bytes_sent < flow_rate * duration + burst_credit + mtu
*
* where `burst_credit` is defined as:
*
* burst_credit = max((9 * mtu + 1) - flow_rate, 0)
*
* and that the sender never sends more than max(10*mtu, flow_rate) per every time slice.
*/
typedef struct st_quicly_pacer_t {
/**
* clock
*/
int64_t at;
/**
* amount of credit being spent at `at`
*/
size_t bytes_sent;
} quicly_pacer_t;

#define QUICLY_PACER_CALC_BURST_BYTES(mtu) ((size_t)(mtu)*9 + 1)
#define QUICLY_PACER_SEND_RATE_MULTIPLIER 16
#define QUICLY_PACER_CALC_MULTIPLIER(x) ((uint32_t)((x)*QUICLY_PACER_SEND_RATE_MULTIPLIER + 0.99)) /* round-up */

/**
* resets the pacer
*/
static void quicly_pacer_reset(quicly_pacer_t *pacer);
/**
* returns when the next chunk of data can be sent
*/
static int64_t quicly_pacer_can_send_at(quicly_pacer_t *pacer, uint32_t bytes_per_msec, uint16_t mtu);
/**
* returns the number of bytes that can be sent at this moment
*/
static uint64_t quicly_pacer_get_window(quicly_pacer_t *pacer, int64_t now, uint32_t bytes_per_msec, uint16_t mtu);
/**
* updates the window size available at current time
*/
static void quicly_pacer_consume_window(quicly_pacer_t *pacer, size_t delta);
/**
* Calculates the flow rate as `bytes_per_msec`. The returned value is no less than 1.
* @param multiplier multiplier applied to `cwnd / rtt` in permil
*/
static uint32_t quicly_pacer_calc_send_rate(uint32_t multiplier, uint32_t cwnd, uint32_t rtt);

/* inline definitions */

inline void quicly_pacer_reset(quicly_pacer_t *pacer)
{
pacer->at = INT64_MIN;
pacer->bytes_sent = 0;
}

inline int64_t quicly_pacer_can_send_at(quicly_pacer_t *pacer, uint32_t bytes_per_msec, uint16_t mtu)
{
/* return "now" if we have room in current msec */
size_t burst_size = QUICLY_PACER_CALC_BURST_BYTES(mtu);
size_t burst_credit = burst_size > bytes_per_msec ? burst_size - bytes_per_msec : 0;
if (pacer->bytes_sent < bytes_per_msec + burst_credit)
return 0;

/* calculate delay; the value is rounded down, as it is better for a pacer to be a bit aggressive than not */
int64_t delay = (pacer->bytes_sent - burst_credit) / bytes_per_msec;
assert(delay > 0);
return pacer->at + delay;
}

inline uint64_t quicly_pacer_get_window(quicly_pacer_t *pacer, int64_t now, uint32_t bytes_per_msec, uint16_t mtu)
{
assert(pacer->at <= now);

/* Determine when it is possible to sent one packet. Return if that is a moment in future. */
int64_t can_send_at = quicly_pacer_can_send_at(pacer, bytes_per_msec, mtu);
if (now < can_send_at)
return 0;

/* Calculate burst window, as max(10mtu, bytes_per_msec) */
size_t burst_window = QUICLY_PACER_CALC_BURST_BYTES(mtu);
if (burst_window < bytes_per_msec)
burst_window = bytes_per_msec;

/* Additional amount of data that we can send in `now - restricted_at` milliseconds is that difference multiplied by
* `bytes_per_msec`. Adjust `bytes_sent` by that amount before setting `restricted_at` to `now`. `uint64_t` is used to store
* window and delta so that the multiplication would not overflow assuming that the quiescence period is shorter than 2**32
* milliseconds. */
uint64_t window, delta = (now - pacer->at) * bytes_per_msec;
if (pacer->bytes_sent > delta) {
pacer->bytes_sent -= delta;
window = burst_window > pacer->bytes_sent ? burst_window - pacer->bytes_sent : 1;
} else {
pacer->bytes_sent = 0;
window = burst_window;
}

pacer->at = now;

return window;
}

inline void quicly_pacer_consume_window(quicly_pacer_t *pacer, size_t delta)
{
pacer->bytes_sent += delta;
}

inline uint32_t quicly_pacer_calc_send_rate(uint32_t multiplier, uint32_t cwnd, uint32_t rtt)
{
return ((cwnd + QUICLY_PACER_SEND_RATE_MULTIPLIER - 1) / QUICLY_PACER_SEND_RATE_MULTIPLIER * multiplier + rtt - 1) / rtt;
}

#ifdef __cplusplus
}
#endif

#endif
3 changes: 3 additions & 0 deletions lib/cc-cubic.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <math.h>
#include "quicly/cc.h"
#include "quicly.h"
#include "quicly/pacer.h"

#define QUICLY_MIN_CWND 2

Expand Down Expand Up @@ -77,6 +78,7 @@ static void cubic_on_acked(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t
}

/* Congestion avoidance. */
cc->pacer_multiplier = QUICLY_PACER_CALC_MULTIPLIER(1.2);
cubic_float_t t_sec = calc_cubic_t(cc, now);
cubic_float_t rtt_sec = loss->rtt.smoothed / (cubic_float_t)1000; /* ms -> s */

Expand Down Expand Up @@ -165,6 +167,7 @@ static void cubic_reset(quicly_cc_t *cc, uint32_t initcwnd)
cc->type = &quicly_cc_type_cubic;
cc->cwnd = cc->cwnd_initial = cc->cwnd_maximum = initcwnd;
cc->ssthresh = cc->cwnd_minimum = UINT32_MAX;
cc->pacer_multiplier = QUICLY_PACER_CALC_MULTIPLIER(2);
}

static int cubic_on_switch(quicly_cc_t *cc)
Expand Down
5 changes: 5 additions & 0 deletions lib/cc-pico.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* IN THE SOFTWARE.
*/
#include <math.h>
#include "quicly/pacer.h"
#include "quicly/cc.h"
#include "quicly.h"

Expand Down Expand Up @@ -102,6 +103,9 @@ static void pico_on_lost(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t by
return;
cc->recovery_end = next_pn;

/* switch pacer to congestion avoidance mode the moment loss is observed */
cc->pacer_multiplier = QUICLY_PACER_CALC_MULTIPLIER(1.2);

++cc->num_loss_episodes;
if (cc->cwnd_exiting_slow_start == 0)
cc->cwnd_exiting_slow_start = cc->cwnd;
Expand Down Expand Up @@ -144,6 +148,7 @@ static void pico_reset(quicly_cc_t *cc, uint32_t initcwnd)
.cwnd_maximum = initcwnd,
.cwnd_minimum = UINT32_MAX,
.ssthresh = UINT32_MAX,
.pacer_multiplier = QUICLY_PACER_CALC_MULTIPLIER(2),
};
pico_init_pico_state(cc, 0);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/cc-reno.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/
#include "quicly/cc.h"
#include "quicly.h"
#include "quicly/pacer.h"

/* TODO: Avoid increase if sender was application limited. */
static void reno_on_acked(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t bytes, uint64_t largest_acked, uint32_t inflight,
Expand All @@ -39,6 +40,7 @@ static void reno_on_acked(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t b
return;
}
/* Congestion avoidance. */
cc->pacer_multiplier = QUICLY_PACER_CALC_MULTIPLIER(1.2);
kazuho marked this conversation as resolved.
Show resolved Hide resolved
cc->state.reno.stash += bytes;
if (cc->state.reno.stash < cc->cwnd)
return;
Expand Down Expand Up @@ -90,6 +92,7 @@ static void reno_reset(quicly_cc_t *cc, uint32_t initcwnd)
cc->type = &quicly_cc_type_reno;
cc->cwnd = cc->cwnd_initial = cc->cwnd_maximum = initcwnd;
cc->ssthresh = cc->cwnd_minimum = UINT32_MAX;
cc->pacer_multiplier = QUICLY_PACER_CALC_MULTIPLIER(2);
}

static int reno_on_switch(quicly_cc_t *cc)
Expand Down
2 changes: 2 additions & 0 deletions lib/defaults.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const quicly_context_t quicly_spec_context = {NULL,
DEFAULT_MAX_INITIAL_HANDSHAKE_PACKETS,
0, /* enlarge_client_hello */
1, /* enable_ecn */
0, /* use_pacing */
NULL,
NULL, /* on_stream_open */
&quicly_default_stream_scheduler,
Expand Down Expand Up @@ -82,6 +83,7 @@ const quicly_context_t quicly_performant_context = {NULL,
DEFAULT_MAX_INITIAL_HANDSHAKE_PACKETS,
0, /* enlarge_client_hello */
1, /* enable_ecn */
0, /* use_pacing */
NULL,
NULL, /* on_stream_open */
&quicly_default_stream_scheduler,
Expand Down
Loading
Loading