Skip to content

Commit

Permalink
Add dummy congestion controller for testing and experiments (#274)
Browse files Browse the repository at this point in the history
  • Loading branch information
iyangsj committed May 23, 2024
1 parent 40c816d commit f449431
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 1 deletion.
7 changes: 6 additions & 1 deletion interop/run_endpoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ BBR3)
COPA)
CC_ALGOR="COPA"
;;
DUMMY)
CC_ALGOR="DUMMY"
;;
*)
;;
esac

COMMON_ARGS="--keylog-file $SSLKEYLOGFILE --log-level DEBUG --log-file $LOG_DIR/$ROLE.log --idle-timeout 30000 --handshake-timeout 30000 --initial-rtt 100 --congestion-control-algor $CC_ALGOR"
# Note: You can add extra command-line options to tquic_client/tquic_sever by
# using the `EXTRA_ARGS` environment variable.
COMMON_ARGS="--keylog-file $SSLKEYLOGFILE --log-level DEBUG --log-file $LOG_DIR/$ROLE.log --idle-timeout 30000 --handshake-timeout 30000 --initial-rtt 100 --congestion-control-algor $CC_ALGOR $EXTRA_ARGS"

if [ "$TESTCASE" != "transfer" ]; then
COMMON_ARGS="$COMMON_ARGS --qlog-dir $QLOG_DIR"
Expand Down
12 changes: 12 additions & 0 deletions src/congestion_control/congestion_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub use copa::Copa;
pub use copa::CopaConfig;
pub use cubic::Cubic;
pub use cubic::CubicConfig;
pub use dummy::Dummy;
pub use hystart_plus_plus::HystartPlusPlus;

/// Available congestion control algorithms.
Expand Down Expand Up @@ -61,6 +62,10 @@ pub enum CongestionControlAlgorithm {
/// and delay can be configured via a user-specified parameter.
/// (Experimental)
Copa,

/// Dummy is a simple congestion controller with a static congestion window.
/// It is intended to be used for testing and experiments.
Dummy,
}

impl FromStr for CongestionControlAlgorithm {
Expand All @@ -75,6 +80,8 @@ impl FromStr for CongestionControlAlgorithm {
Ok(CongestionControlAlgorithm::Bbr3)
} else if algor.eq_ignore_ascii_case("copa") {
Ok(CongestionControlAlgorithm::Copa)
} else if algor.eq_ignore_ascii_case("dummy") {
Ok(CongestionControlAlgorithm::Dummy)
} else {
Err(Error::InvalidConfig("unknown".into()))
}
Expand Down Expand Up @@ -208,6 +215,7 @@ pub fn build_congestion_controller(conf: &RecoveryConfig) -> Box<dyn CongestionC
Some(conf.initial_rtt),
max_datagram_size,
))),
CongestionControlAlgorithm::Dummy => Box::new(Dummy::new(initial_cwnd)),
}
}

Expand Down Expand Up @@ -235,6 +243,9 @@ mod tests {
("copa", Ok(CongestionControlAlgorithm::Copa)),
("Copa", Ok(CongestionControlAlgorithm::Copa)),
("COPA", Ok(CongestionControlAlgorithm::Copa)),
("dummy", Ok(CongestionControlAlgorithm::Dummy)),
("Dummy", Ok(CongestionControlAlgorithm::Dummy)),
("DUMMY", Ok(CongestionControlAlgorithm::Dummy)),
("cubci", Err(Error::InvalidConfig("unknown".into()))),
];

Expand Down Expand Up @@ -287,6 +298,7 @@ mod bbr3;
mod copa;
mod cubic;
mod delivery_rate;
mod dummy;
mod hystart_plus_plus;
mod minmax;
mod pacing;
179 changes: 179 additions & 0 deletions src/congestion_control/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Copyright (c) 2024 The TQUIC Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(unused_variables)]

use std::time::Instant;

use super::CongestionController;
use super::CongestionStats;
use crate::connection::rtt::RttEstimator;
use crate::connection::space::SentPacket;

/// Dummy is a simple congestion controller with a static congestion window.
/// It is intended to be used for testing and experiments.
#[derive(Debug)]
pub struct Dummy {
/// Congestion window in bytes.
cwnd: u64,

/// Congestion statistics.
stats: CongestionStats,
}

impl Dummy {
pub fn new(initial_cwnd: u64) -> Self {
Self {
cwnd: initial_cwnd,
stats: Default::default(),
}
}
}

impl CongestionController for Dummy {
fn name(&self) -> &str {
"DUMMY"
}

fn on_sent(&mut self, now: Instant, packet: &mut SentPacket, bytes_in_flight: u64) {
let sent_bytes = packet.sent_size as u64;
self.stats.bytes_in_flight = bytes_in_flight;
self.stats.bytes_sent_in_total = self.stats.bytes_sent_in_total.saturating_add(sent_bytes);
}

fn begin_ack(&mut self, now: Instant, bytes_in_flight: u64) {
// Do nothing.
}

fn on_ack(
&mut self,
packet: &mut SentPacket,
now: Instant,
app_limited: bool,
rtt: &RttEstimator,
bytes_in_flight: u64,
) {
let acked_bytes = packet.sent_size as u64;
self.stats.bytes_in_flight = bytes_in_flight;
self.stats.bytes_acked_in_total =
self.stats.bytes_acked_in_total.saturating_add(acked_bytes);
}

fn end_ack(&mut self) {
// Do nothing.
}

fn on_congestion_event(
&mut self,
now: Instant,
packet: &SentPacket,
is_persistent_congestion: bool,
lost_bytes: u64,
bytes_in_flight: u64,
) {
self.stats.bytes_lost_in_total = self.stats.bytes_lost_in_total.saturating_add(lost_bytes);
self.stats.bytes_in_flight = bytes_in_flight;
}

fn in_slow_start(&self) -> bool {
false
}

fn in_recovery(&self, sent_time: Instant) -> bool {
false
}

fn congestion_window(&self) -> u64 {
self.cwnd
}

fn initial_window(&self) -> u64 {
self.cwnd
}

fn minimal_window(&self) -> u64 {
self.cwnd
}

fn stats(&self) -> &CongestionStats {
&self.stats
}

fn pacing_rate(&self) -> Option<u64> {
None
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;

#[test]
fn dummy_init() {
let d = Dummy::new(1200 * 10);
assert_eq!(d.name(), "DUMMY");
assert_eq!(d.congestion_window(), 1200 * 10);
assert_eq!(d.initial_window(), 1200 * 10);
assert_eq!(d.minimal_window(), 1200 * 10);

assert_eq!(d.in_slow_start(), false);
assert_eq!(d.in_recovery(Instant::now()), false);
assert_eq!(d.stats().bytes_in_flight, 0);
assert_eq!(d.pacing_rate(), None);
}

#[test]
fn dummy_stats() {
let mut d = Dummy::new(1200 * 10);
let rtt = Duration::from_millis(100);
let rtt_estimator = RttEstimator::new(rtt);
let now = Instant::now();

// Sent and acked a packet
let mut pkt = SentPacket {
pkt_num: 0,
ack_eliciting: true,
in_flight: true,
sent_size: 1200,
..SentPacket::default()
};
d.on_sent(now, &mut pkt, 1200);
assert_eq!(d.stats().bytes_in_flight, 1200);
assert_eq!(d.stats().bytes_sent_in_total, 1200);

let now = now + rtt;
d.begin_ack(now, 1200);
d.on_ack(&mut pkt, now, true, &rtt_estimator, 0);
d.end_ack();
assert_eq!(d.stats().bytes_in_flight, 0);
assert_eq!(d.stats().bytes_acked_in_total, 1200);

// Sent and lost a packet
let mut pkt = SentPacket {
pkt_num: 0,
ack_eliciting: true,
in_flight: true,
sent_size: 1400,
..SentPacket::default()
};
d.on_sent(now, &mut pkt, 1400);
assert_eq!(d.stats().bytes_in_flight, 1400);
assert_eq!(d.stats().bytes_sent_in_total, 2600);

d.on_congestion_event(now, &pkt, false, 1400, 0);
assert_eq!(d.stats().bytes_in_flight, 0);
assert_eq!(d.stats().bytes_lost_in_total, 1400);
}
}
14 changes: 14 additions & 0 deletions src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2645,6 +2645,20 @@ mod tests {
Ok(())
}

#[test]
fn transfer_single_stream_dummy_with_packet_loss() -> Result<()> {
let mut t = TestPair::new();

let mut case_conf = CaseConf::default();
case_conf.request_num = 1;
case_conf.request_size = 1024 * 16;
case_conf.packet_loss = 1;
case_conf.cc_algor = CongestionControlAlgorithm::Dummy;

t.run_with_test_config(case_conf)?;
Ok(())
}

#[test]
fn transfer_single_stream_with_packet_delay() -> Result<()> {
let mut t = TestPair::new();
Expand Down

0 comments on commit f449431

Please sign in to comment.