Skip to content

Commit

Permalink
Add configuration for pacing granularity (#335)
Browse files Browse the repository at this point in the history
  • Loading branch information
iyangsj authored Jul 17, 2024
1 parent b649842 commit e9cbe5d
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 24 deletions.
12 changes: 12 additions & 0 deletions include/tquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,18 @@ void quic_config_set_bbr_probe_bw_cwnd_gain(struct quic_config_t *config, double
*/
void quic_config_set_initial_rtt(struct quic_config_t *config, uint64_t v);

/**
* Enable pacing to smooth the flow of packets sent onto the network.
* The default value is true.
*/
void quic_config_enable_pacing(struct quic_config_t *config, bool v);

/**
* Set clock granularity used by the pacer.
* The default value is 10 milliseconds.
*/
void quic_config_set_pacing_granularity(struct quic_config_t *config, uint64_t v);

/**
* Set the linear factor for calculating the probe timeout.
* The endpoint do not backoff the first `v` consecutive probe timeouts.
Expand Down
58 changes: 35 additions & 23 deletions src/congestion_control/pacing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,40 @@ pub struct Pacer {
/// Bucket capacity (bytes). Bytes that could burst during a pacing granularity
capacity: u64,

/// last congestion window, bytes
last_cwnd: u64,

/// available tokens, bytes
tokens: u64,

/// last congestion window, bytes
last_cwnd: u64,

/// last schedule time
last_sched_time: Instant,

/// Pacing granularity
granularity: Duration,
}

impl Pacer {
/// Generate a pacer (for each path)
pub fn new(enabled: bool, srtt: Duration, cwnd: u64, mtu: u64, now: Instant) -> Self {
let capacity = Self::calc_capacity(cwnd, srtt, mtu);

Self {
pub fn new(
enabled: bool,
srtt: Duration,
cwnd: u64,
mtu: u64,
now: Instant,
granularity: Duration,
) -> Self {
let mut pacer = Pacer {
enabled,
capacity,
capacity: 0,
tokens: 0,
last_cwnd: cwnd,
tokens: capacity,
last_sched_time: now,
}
granularity,
};
pacer.update_capacity(cwnd, srtt, mtu);
pacer.tokens = pacer.capacity;
pacer
}

/// Build a pacer controller.
Expand All @@ -76,6 +88,7 @@ impl Pacer {
.saturating_mul(conf.max_datagram_size as u64),
conf.max_datagram_size as u64,
Instant::now(),
conf.pacing_granularity,
)
}

Expand Down Expand Up @@ -111,7 +124,7 @@ impl Pacer {

// Update capacity and tokens if necessary
if cwnd != self.last_cwnd {
self.capacity = Self::calc_capacity(cwnd, srtt, mtu);
self.update_capacity(cwnd, srtt, mtu);
self.tokens = self.capacity.min(self.tokens);
self.last_cwnd = cwnd;
}
Expand Down Expand Up @@ -139,14 +152,13 @@ impl Pacer {
Some(self.last_sched_time + Duration::from_nanos(time_to_wait))
}

fn calc_capacity(cwnd: u64, srtt: Duration, mtu: u64) -> u64 {
fn update_capacity(&mut self, cwnd: u64, srtt: Duration, mtu: u64) {
// Note: the bound operation would limit the average pacing rate to
// [MIN_BURST_PACKET_NUM * mtu / srtt, MAX_BURST_PACKET_NUM * mtu / srtt]
// the minimal pacing rate may be too large in some cases.
let capacity =
(cwnd as u128 * PACING_GRANULARITY.as_nanos() / srtt.as_nanos().max(1_000_000)) as u64;

capacity.clamp(MIN_BURST_PACKET_NUM * mtu, MAX_BURST_PACKET_NUM * mtu)
(cwnd as u128 * self.granularity.as_nanos() / srtt.as_nanos().max(1_000_000)) as u64;
self.capacity = capacity.clamp(MIN_BURST_PACKET_NUM * mtu, MAX_BURST_PACKET_NUM * mtu)
}
}

Expand All @@ -162,7 +174,7 @@ mod tests {
let now = Instant::now();

let cwnd: u64 = 20 * mtu;
let p = Pacer::new(enabled, srtt, cwnd, mtu, now);
let p = Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY);
assert!(p.enabled() == true);
assert_eq!(p.capacity, p.tokens);
assert_eq!(
Expand All @@ -171,13 +183,13 @@ mod tests {
);

let cwnd: u64 = 1 * mtu;
let p = Pacer::new(enabled, srtt, cwnd, mtu, now);
let p = Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY);
assert!(p.enabled() == true);
assert_eq!(p.capacity, p.tokens);
assert_eq!(p.capacity, MIN_BURST_PACKET_NUM * mtu);

let cwnd: u64 = 200 * mtu;
let p = Pacer::new(enabled, srtt, cwnd, mtu, now);
let p = Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY);
assert!(p.enabled() == true);
assert_eq!(p.capacity, p.tokens);
assert_eq!(p.capacity, MAX_BURST_PACKET_NUM * mtu);
Expand All @@ -193,7 +205,7 @@ mod tests {
let bytes_to_send: u64 = 1000;
let pacing_rate: u64 = 1000000;

let mut p = Pacer::new(enabled, srtt, cwnd, mtu, now);
let mut p = Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY);

assert_eq!(p.enabled(), false);
assert_eq!(p.capacity, 20 * 1500);
Expand All @@ -216,7 +228,7 @@ mod tests {

// Abnormal input
assert_eq!(
Pacer::new(enabled, srtt, cwnd, mtu, now).schedule(
Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY).schedule(
bytes_to_send,
pacing_rate,
Duration::ZERO,
Expand All @@ -227,7 +239,7 @@ mod tests {
None
);
assert_eq!(
Pacer::new(enabled, srtt, cwnd, mtu, now).schedule(
Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY).schedule(
bytes_to_send,
pacing_rate,
srtt,
Expand All @@ -239,7 +251,7 @@ mod tests {
);

// Congestion window changes
let mut p = Pacer::new(enabled, srtt, cwnd, mtu, now);
let mut p = Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY);
assert_eq!(p.capacity, cwnd);
assert_eq!(p.capacity, p.tokens);

Expand All @@ -251,7 +263,7 @@ mod tests {
assert_eq!(p.tokens, cwnd); // do not change tokens

// Schedule and wait cases
let mut p = Pacer::new(enabled, srtt, cwnd, mtu, now);
let mut p = Pacer::new(enabled, srtt, cwnd, mtu, now, PACING_GRANULARITY);
assert_eq!(p.capacity, 10 * mtu);
assert_eq!(p.tokens, 10 * mtu);

Expand Down
14 changes: 14 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,20 @@ pub extern "C" fn quic_config_set_initial_rtt(config: &mut Config, v: u64) {
config.set_initial_rtt(v);
}

/// Enable pacing to smooth the flow of packets sent onto the network.
/// The default value is true.
#[no_mangle]
pub extern "C" fn quic_config_enable_pacing(config: &mut Config, v: bool) {
config.enable_pacing(v);
}

/// Set clock granularity used by the pacer.
/// The default value is 10 milliseconds.
#[no_mangle]
pub extern "C" fn quic_config_set_pacing_granularity(config: &mut Config, v: u64) {
config.set_pacing_granularity(v);
}

/// Set the linear factor for calculating the probe timeout.
/// The endpoint do not backoff the first `v` consecutive probe timeouts.
/// The default value is `0`.
Expand Down
13 changes: 12 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,11 +559,18 @@ impl Config {
}

/// Enable pacing to smooth the flow of packets sent onto the network.
/// default value is true.
/// The default value is true.
pub fn enable_pacing(&mut self, v: bool) {
self.recovery.enable_pacing = v;
}

/// Set clock granularity used by the pacer.
/// The default value is 1 milliseconds.
pub fn set_pacing_granularity(&mut self, millis: u64) {
self.recovery.pacing_granularity =
cmp::max(Duration::from_millis(millis), TIMER_GRANULARITY);
}

/// Set the linear factor for calculating the probe timeout.
/// The endpoint do not backoff the first `v` consecutive probe timeouts.
/// The default value is `0`.
Expand Down Expand Up @@ -784,6 +791,9 @@ pub struct RecoveryConfig {
/// Enable pacing to smooth the flow of packets sent onto the network.
pub enable_pacing: bool,

/// Clock granularity used by the pacer.
pub pacing_granularity: Duration,

/// Linear factor for calculating the probe timeout.
pub pto_linear_factor: u64,

Expand All @@ -808,6 +818,7 @@ impl Default for RecoveryConfig {
bbr_probe_bw_cwnd_gain: 2.0,
initial_rtt: INITIAL_RTT,
enable_pacing: true,
pacing_granularity: time::Duration::from_millis(1),
pto_linear_factor: DEFAULT_PTO_LINEAR_FACTOR,
max_pto: MAX_PTO,
}
Expand Down

0 comments on commit e9cbe5d

Please sign in to comment.