use std::cell::{Cell, RefCell};
use std::net::SocketAddr;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use rand_chacha::ChaCha20Rng;
use rand_chacha::rand_core::SeedableRng;
use slither::packet::ReferenceSuite;
use slither::prelude::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::{TcpListener, TcpStream, UdpSocket};
const CHUNK: usize = 64 << 10;
const BULK_RAMP: Duration = Duration::from_secs(2);
const BULK_WINDOW: Duration = Duration::from_secs(4);
const BULK_SAMPLES: usize = 5;
const TCP_RELAY_CHUNK: usize = 64 << 10;
const TCP_RELAY_DEPTH: usize = 1024;
const UDP_RELAY_BUF: usize = 2048;
const PING_ITERS: usize = 2000;
const PING_WARMUP: usize = 100;
const PING_PAYLOAD: usize = 64;
const MUX_STREAMS: [usize; 3] = [1, 4, 16];
const RAISED_STREAM: u64 = 8 << 20;
const RAISED_CONN: u64 = 16 << 20;
const SWEEP: [Windows; 5] = [
WINDOWS_DEFAULT,
Windows {
stream: 512 << 10,
connection: 2 << 20,
label: "512Ki/2Mi",
},
Windows {
stream: 1 << 20,
connection: 4 << 20,
label: "1Mi/4Mi",
},
Windows {
stream: 2 << 20,
connection: 8 << 20,
label: "2Mi/8Mi",
},
WINDOWS_RAISED,
];
type Id = SoftwareIdentity<ReferenceSuite, ChaCha20Rng>;
type Conn = Connection<ReferenceSuite>;
const MIB: f64 = 1024.0 * 1024.0;
struct Row {
scenario: &'static str,
proto: &'static str,
rtt_ms: u64,
windows: &'static str,
streams: usize,
unit: &'static str,
prec: usize,
samples: Vec<f64>,
note: Option<String>,
}
fn percentile(sorted: &[f64], q: f64) -> f64 {
let rank = (q * sorted.len() as f64).ceil() as usize;
sorted[rank.clamp(1, sorted.len()) - 1]
}
impl Row {
fn emit(&self) {
assert!(
!self.samples.is_empty(),
"{} produced no samples",
self.scenario
);
let mut s = self.samples.clone();
s.sort_by(f64::total_cmp);
let p = |q: f64| format!("{:.*}", self.prec, percentile(&s, q));
let tail = |q: f64, need: usize| {
if s.len() >= need {
p(q)
} else {
"-".to_string()
}
};
let note = match &self.note {
Some(n) => format!(" note={n}"),
None => String::new(),
};
println!(
"BENCH scenario={} proto={} rtt_ms={} windows={} streams={} n={} \
p50={} p99={} p999={} min={} max={} unit={}{}",
self.scenario,
self.proto,
self.rtt_ms,
self.windows,
self.streams,
s.len(),
p(0.50),
tail(0.99, 100),
tail(0.999, 1000),
format_args!("{:.*}", self.prec, s[0]),
format_args!("{:.*}", self.prec, s[s.len() - 1]),
self.unit,
note,
);
}
}
enum RelayCmd {
Udp {
server: SocketAddr,
delay: Duration,
reply: std::sync::mpsc::SyncSender<SocketAddr>,
},
Tcp {
server: SocketAddr,
delay: Duration,
reply: std::sync::mpsc::SyncSender<SocketAddr>,
},
}
struct Relay {
tx: tokio::sync::mpsc::UnboundedSender<RelayCmd>,
}
impl Relay {
fn start() -> Self {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<RelayCmd>();
std::thread::Builder::new()
.name("bench-relay".into())
.spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("the relay's own current-thread runtime");
rt.block_on(async move {
while let Some(cmd) = rx.recv().await {
match cmd {
RelayCmd::Udp {
server,
delay,
reply,
} => {
let front = udp_relay(server, delay).await;
let _ = reply.send(front);
}
RelayCmd::Tcp {
server,
delay,
reply,
} => {
let front = tcp_relay(server, delay).await;
let _ = reply.send(front);
}
}
}
});
})
.expect("spawn the relay thread");
Self { tx }
}
fn front(
&self,
cmd: impl FnOnce(std::sync::mpsc::SyncSender<SocketAddr>) -> RelayCmd,
) -> SocketAddr {
let (reply, wait) = std::sync::mpsc::sync_channel(1);
self.tx.send(cmd(reply)).expect("the relay thread is alive");
wait.recv().expect("the relay reported its front address")
}
fn udp(&self, server: SocketAddr, delay: Duration) -> SocketAddr {
self.front(|reply| RelayCmd::Udp {
server,
delay,
reply,
})
}
fn tcp(&self, server: SocketAddr, delay: Duration) -> SocketAddr {
self.front(|reply| RelayCmd::Tcp {
server,
delay,
reply,
})
}
}
async fn udp_relay(server: SocketAddr, delay: Duration) -> SocketAddr {
let front = Arc::new(
UdpSocket::bind("127.0.0.1:0")
.await
.expect("bind the relay's client-facing socket"),
);
let back = Arc::new(
UdpSocket::bind("127.0.0.1:0")
.await
.expect("bind the relay's server-facing socket"),
);
let front_addr = front.local_addr().expect("the relay's front address");
let client: Arc<Mutex<Option<SocketAddr>>> = Arc::new(Mutex::new(None));
{
let (q_tx, mut q_rx) =
tokio::sync::mpsc::unbounded_channel::<(tokio::time::Instant, Vec<u8>)>();
let (front, back, client) = (Arc::clone(&front), Arc::clone(&back), Arc::clone(&client));
tokio::spawn(async move {
let mut buf = vec![0u8; UDP_RELAY_BUF];
while let Ok((n, from)) = front.recv_from(&mut buf).await {
*client.lock().expect("relay address cell") = Some(from);
if q_tx
.send((tokio::time::Instant::now() + delay, buf[..n].to_vec()))
.is_err()
{
break;
}
}
});
tokio::spawn(async move {
while let Some((due, data)) = q_rx.recv().await {
tokio::time::sleep_until(due).await;
if back.send_to(&data, server).await.is_err() {
break;
}
}
});
}
{
let (q_tx, mut q_rx) =
tokio::sync::mpsc::unbounded_channel::<(tokio::time::Instant, Vec<u8>)>();
tokio::spawn(async move {
let mut buf = vec![0u8; UDP_RELAY_BUF];
while let Ok((n, _from)) = back.recv_from(&mut buf).await {
if q_tx
.send((tokio::time::Instant::now() + delay, buf[..n].to_vec()))
.is_err()
{
break;
}
}
});
tokio::spawn(async move {
while let Some((due, data)) = q_rx.recv().await {
tokio::time::sleep_until(due).await;
let to = *client.lock().expect("relay address cell");
let Some(to) = to else { continue };
if front.send_to(&data, to).await.is_err() {
break;
}
}
});
}
front_addr
}
async fn tcp_relay(server: SocketAddr, delay: Duration) -> SocketAddr {
let listener = TcpListener::bind("127.0.0.1:0")
.await
.expect("bind the relay's listener");
let front_addr = listener.local_addr().expect("the relay's front address");
tokio::spawn(async move {
while let Ok((client, _)) = listener.accept().await {
let Ok(upstream) = TcpStream::connect(server).await else {
break;
};
let _ = client.set_nodelay(true);
let _ = upstream.set_nodelay(true);
let (cr, cw) = client.into_split();
let (ur, uw) = upstream.into_split();
tokio::spawn(tcp_pump(cr, uw, delay));
tokio::spawn(tcp_pump(ur, cw, delay));
}
});
front_addr
}
async fn tcp_pump(mut r: OwnedReadHalf, mut w: OwnedWriteHalf, delay: Duration) {
let (q_tx, mut q_rx) =
tokio::sync::mpsc::channel::<(tokio::time::Instant, Vec<u8>)>(TCP_RELAY_DEPTH);
let writer = tokio::spawn(async move {
while let Some((due, data)) = q_rx.recv().await {
tokio::time::sleep_until(due).await;
if w.write_all(&data).await.is_err() {
break;
}
}
let _ = w.shutdown().await;
});
let mut buf = vec![0u8; TCP_RELAY_CHUNK];
loop {
match r.read(&mut buf).await {
Ok(0) | Err(_) => break,
Ok(n) => {
if q_tx
.send((tokio::time::Instant::now() + delay, buf[..n].to_vec()))
.await
.is_err()
{
break;
}
}
}
}
drop(q_tx);
let _ = writer.await;
}
struct BulkMeter {
ramp_end: Instant,
window: Duration,
wanted: usize,
armed: Option<Instant>,
bytes: u64,
total: u64,
out: Vec<f64>,
}
impl BulkMeter {
fn new(ramp: Duration, window: Duration, wanted: usize) -> Self {
Self {
ramp_end: Instant::now() + ramp,
window,
wanted,
armed: None,
bytes: 0,
total: 0,
out: Vec::with_capacity(wanted),
}
}
fn record(&mut self, n: usize) -> bool {
if self.out.len() >= self.wanted {
return true;
}
let now = Instant::now();
if now < self.ramp_end {
return false;
}
let Some(start) = self.armed else {
self.armed = Some(now);
self.bytes = 0;
return false;
};
self.bytes += n as u64;
let elapsed = now.duration_since(start);
if elapsed >= self.window {
self.out
.push((self.bytes as f64 / MIB) / elapsed.as_secs_f64());
self.total += self.bytes;
self.armed = Some(now);
self.bytes = 0;
}
self.out.len() >= self.wanted
}
}
#[derive(Clone)]
struct BulkState {
meter: Rc<RefCell<BulkMeter>>,
done: Rc<Cell<bool>>,
failure: Rc<RefCell<Option<String>>>,
wires: Option<(Rc<WireCounters>, Rc<WireCounters>)>,
edges: Rc<Cell<Edges>>,
}
#[derive(Clone, Copy, Default)]
struct Edges {
start_a: [u64; 4],
start_b: [u64; 4],
end_a: [u64; 4],
end_b: [u64; 4],
}
impl BulkState {
fn new(samples: usize) -> Self {
Self {
meter: Rc::new(RefCell::new(BulkMeter::new(
BULK_RAMP,
BULK_WINDOW,
samples,
))),
done: Rc::new(Cell::new(false)),
failure: Rc::new(RefCell::new(None)),
wires: None,
edges: Rc::new(Cell::new(Edges::default())),
}
}
fn with_wires(mut self, a: &Rc<WireCounters>, b: &Rc<WireCounters>) -> Self {
self.wires = Some((Rc::clone(a), Rc::clone(b)));
self
}
fn snapshot(&self) -> ([u64; 4], [u64; 4]) {
match &self.wires {
Some((a, b)) => (a.snapshot(), b.snapshot()),
None => ([0; 4], [0; 4]),
}
}
fn record(&self, n: usize) {
let mut m = self.meter.borrow_mut();
let was_armed = m.armed.is_some();
let finished = m.record(n);
drop(m);
if !was_armed && self.meter.borrow().armed.is_some() {
let (start_a, start_b) = self.snapshot();
let edges = self.edges.get();
self.edges.set(Edges {
start_a,
start_b,
..edges
});
}
if finished && !self.done.get() {
let (end_a, end_b) = self.snapshot();
let edges = self.edges.get();
self.edges.set(Edges {
end_a,
end_b,
..edges
});
self.done.set(true);
}
}
fn fail(&self, why: impl Into<String>) {
let mut slot = self.failure.borrow_mut();
if slot.is_none() {
*slot = Some(why.into());
}
}
fn failed(&self) -> bool {
self.failure.borrow().is_some()
}
fn finish(self) -> Vec<f64> {
let failure = self.failure.borrow().clone();
let out = self.meter.borrow().out.clone();
let wanted = self.meter.borrow().wanted;
assert!(
failure.is_none(),
"the cell died after {} of {wanted} windows: {}",
out.len(),
failure.unwrap_or_default()
);
assert!(
out.len() >= wanted,
"the cell produced {} of {wanted} windows and reported no cause — \
the transfer ended without the meter being satisfied",
out.len()
);
out
}
fn wire_note(&self) -> Option<String> {
self.wires.as_ref()?;
let e = self.edges.get();
let payload = self.meter.borrow().total;
if payload == 0 {
return None;
}
let out_dgrams = e.end_a[0].saturating_sub(e.start_a[0]);
let out_bytes = e.end_a[1].saturating_sub(e.start_a[1]);
let in_dgrams = e.end_b[2].saturating_sub(e.start_b[2]);
let ack_dgrams = e.end_b[0].saturating_sub(e.start_b[0]);
let loss = if out_dgrams == 0 {
0.0
} else {
1.0 - (in_dgrams as f64 / out_dgrams as f64)
};
Some(format!(
"amp={:.3},loss={:.4},dg_out={out_dgrams},dg_in={in_dgrams},ack_dg={ack_dgrams},mtu={:.0}",
out_bytes as f64 / payload as f64,
loss,
if out_dgrams == 0 {
0.0
} else {
out_bytes as f64 / out_dgrams as f64
},
))
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct Windows {
stream: u64,
connection: u64,
label: &'static str,
}
const WINDOWS_DEFAULT: Windows = Windows {
stream: slither::constants::INITIAL_MAX_STREAM_DATA,
connection: slither::constants::INITIAL_MAX_DATA,
label: "default",
};
const WINDOWS_RAISED: Windows = Windows {
stream: RAISED_STREAM,
connection: RAISED_CONN,
label: "8Mi/16Mi",
};
impl Windows {
fn config(self) -> Config {
Config::new()
.with_flow_windows(self.stream, self.connection)
.expect("a raise within the varint bound, stream <= connection")
}
}
#[derive(Default)]
struct WireCounters {
out_dgrams: Cell<u64>,
out_bytes: Cell<u64>,
in_dgrams: Cell<u64>,
in_bytes: Cell<u64>,
}
impl WireCounters {
fn snapshot(&self) -> [u64; 4] {
[
self.out_dgrams.get(),
self.out_bytes.get(),
self.in_dgrams.get(),
self.in_bytes.get(),
]
}
}
struct CountingWire {
inner: UdpSocket,
counters: Rc<WireCounters>,
}
impl Wire for CountingWire {
async fn send_to(&self, buf: &[u8], addr: SocketAddr) -> std::io::Result<usize> {
let result = self.inner.send_to(buf, addr).await;
if let Ok(n) = &result {
let c = &self.counters;
c.out_dgrams.set(c.out_dgrams.get() + 1);
c.out_bytes.set(c.out_bytes.get() + *n as u64);
}
result
}
async fn recv_from(&self, buf: &mut [u8]) -> std::io::Result<(usize, SocketAddr)> {
let result = self.inner.recv_from(buf).await;
if let Ok((n, _)) = &result {
let c = &self.counters;
c.in_dgrams.set(c.in_dgrams.get() + 1);
c.in_bytes.set(c.in_bytes.get() + *n as u64);
}
result
}
}
struct SlitherLink {
_ep_a: Endpoint<Id>,
_ep_b: Endpoint<Id>,
ca: Conn,
cb: Conn,
wire_a: Rc<WireCounters>,
wire_b: Rc<WireCounters>,
}
async fn slither_link(
relay: Option<(&Relay, Duration)>,
windows: Windows,
seed: u8,
) -> SlitherLink {
let sock_a = UdpSocket::bind("127.0.0.1:0")
.await
.expect("bind the initiator socket");
let sock_b = UdpSocket::bind("127.0.0.1:0")
.await
.expect("bind the responder socket");
let addr_b = sock_b.local_addr().expect("the responder's bound address");
let dial = match relay {
Some((r, d)) => r.udp(addr_b, d),
None => addr_b,
};
let id_a: Id = SoftwareIdentity::generate(ChaCha20Rng::from_seed([seed ^ 0xA1; 32]))
.expect("a seeded ChaCha20 stream yields a valid P-256 scalar");
let id_b: Id = SoftwareIdentity::generate(ChaCha20Rng::from_seed([seed ^ 0xB2; 32]))
.expect("a seeded ChaCha20 stream yields a valid P-256 scalar");
let pk_b = *Identity::public_static(&id_b);
let wire_a = Rc::new(WireCounters::default());
let wire_b = Rc::new(WireCounters::default());
let ep_a: Endpoint<Id> = Endpoint::builder()
.identity(id_a)
.wire(CountingWire {
inner: sock_a,
counters: Rc::clone(&wire_a),
})
.config(windows.config())
.rng_seed([seed ^ 0x11; 32])
.build();
let ep_b: Endpoint<Id> = Endpoint::builder()
.identity(id_b)
.wire(CountingWire {
inner: sock_b,
counters: Rc::clone(&wire_b),
})
.config(windows.config())
.rng_seed([seed ^ 0x22; 32])
.build();
let dial_fut = async {
ep_a.connect(dial, pk_b)
.expect("mint the pending connection")
.await
.expect("the dial completed")
};
let accept_fut = async {
let intro = ep_b.accept().await.expect("an introduction arrived");
let claimed = intro.read_identity().await.expect("read_identity");
let proven = claimed.authenticate().await.expect("authenticate");
proven.accept().await.expect("accept")
};
let (ca, cb) = tokio::join!(dial_fut, accept_fut);
SlitherLink {
_ep_a: ep_a,
_ep_b: ep_b,
ca,
cb,
wire_a,
wire_b,
}
}
async fn write_all(tx: &mut SendStream<ReferenceSuite>, buf: &[u8]) -> Result<(), String> {
let mut off = 0;
while off < buf.len() {
match tx.write(&buf[off..]).await {
Ok(0) => panic!("write returned 0 for a non-empty buffer"),
Ok(n) => off += n,
Err(e) => return Err(format!("write failed after {off} B of a chunk: {e}")),
}
}
Ok(())
}
async fn uni_streams(
ca: &Conn,
cb: &Conn,
count: usize,
) -> (
Vec<SendStream<ReferenceSuite>>,
Vec<RecvStream<ReferenceSuite>>,
) {
let mut txs = Vec::with_capacity(count);
for _ in 0..count {
let mut tx = ca.open_uni().await.expect("open_uni");
tx.write(b"\0").await.expect("priming write");
txs.push(tx);
}
let mut rxs = Vec::with_capacity(count);
let mut prime = [0u8; 1];
for _ in 0..count {
let mut rx = cb.accept_uni().await.expect("accept_uni");
let n = rx
.read(&mut prime)
.await
.expect("read the priming byte")
.expect("the priming byte, not end of stream");
assert_eq!(n, 1, "the priming read returned {n} bytes, not 1");
rxs.push(rx);
}
(txs, rxs)
}
async fn slither_bulk(
relay: Option<(&Relay, Duration)>,
windows: Windows,
streams: usize,
seed: u8,
) -> (Vec<f64>, Option<String>) {
let link = slither_link(relay, windows, seed).await;
let (txs, rxs) = uni_streams(&link.ca, &link.cb, streams).await;
let chunk: Rc<Vec<u8>> = Rc::new((0..CHUNK).map(|i| (i % 251) as u8).collect());
let state = BulkState::new(BULK_SAMPLES).with_wires(&link.wire_a, &link.wire_b);
let mut tasks = Vec::with_capacity(streams * 2);
for mut tx in txs {
let (state, chunk) = (state.clone(), Rc::clone(&chunk));
tasks.push(tokio::task::spawn_local(async move {
while !state.done.get() && !state.failed() {
if let Err(why) = write_all(&mut tx, &chunk).await {
state.fail(why);
return;
}
}
let _ = tx.finish().await;
}));
}
for mut rx in rxs {
let state = state.clone();
tasks.push(tokio::task::spawn_local(async move {
let mut scratch = vec![0u8; CHUNK];
loop {
match rx.read(&mut scratch).await {
Ok(Some(n)) => state.record(n),
Ok(None) => return,
Err(e) => return state.fail(format!("read failed: {e}")),
}
}
}));
}
for t in tasks {
let _ = t.await;
}
drop(link);
let note = state.wire_note();
(state.finish(), note)
}
async fn tcp_bulk(relay: Option<(&Relay, Duration)>, nodelay: bool) -> Vec<f64> {
let listener = TcpListener::bind("127.0.0.1:0")
.await
.expect("bind the TCP server");
let server_addr = listener.local_addr().expect("the TCP server's address");
let dial = match relay {
Some((r, d)) => r.tcp(server_addr, d),
None => server_addr,
};
let state = BulkState::new(BULK_SAMPLES);
let server = {
let state = state.clone();
tokio::task::spawn_local(async move {
let (mut sock, _) = listener.accept().await.expect("accept the TCP client");
if nodelay {
sock.set_nodelay(true).expect("TCP_NODELAY on the server");
}
let mut scratch = vec![0u8; CHUNK];
loop {
match sock.read(&mut scratch).await {
Ok(0) => break,
Ok(n) => state.record(n),
Err(e) => return state.fail(format!("TCP read failed: {e}")),
}
}
})
};
let mut client = TcpStream::connect(dial)
.await
.expect("connect the TCP client");
if nodelay {
client.set_nodelay(true).expect("TCP_NODELAY on the client");
}
let chunk: Vec<u8> = (0..CHUNK).map(|i| (i % 251) as u8).collect();
while !state.done.get() && !state.failed() {
if let Err(e) = client.write_all(&chunk).await {
state.fail(format!("TCP write failed: {e}"));
break;
}
}
let _ = client.shutdown().await;
let _ = server.await;
state.finish()
}
async fn slither_establish_once(relay: Option<(&Relay, Duration)>, seed: u8) -> Duration {
let sock_a = UdpSocket::bind("127.0.0.1:0").await.expect("bind a");
let sock_b = UdpSocket::bind("127.0.0.1:0").await.expect("bind b");
let addr_b = sock_b.local_addr().expect("b's address");
let dial = match relay {
Some((r, d)) => r.udp(addr_b, d),
None => addr_b,
};
let id_a: Id = SoftwareIdentity::generate(ChaCha20Rng::from_seed([seed ^ 0xA1; 32]))
.expect("a valid P-256 scalar");
let id_b: Id = SoftwareIdentity::generate(ChaCha20Rng::from_seed([seed ^ 0xB2; 32]))
.expect("a valid P-256 scalar");
let pk_b = *Identity::public_static(&id_b);
let ep_a: Endpoint<Id> = Endpoint::builder()
.identity(id_a)
.wire(sock_a)
.rng_seed([seed ^ 0x11; 32])
.build();
let ep_b: Endpoint<Id> = Endpoint::builder()
.identity(id_b)
.wire(sock_b)
.rng_seed([seed ^ 0x22; 32])
.build();
let t0 = Instant::now();
let dial_fut = async {
ep_a.connect(dial, pk_b)
.expect("mint the pending connection")
.await
.expect("the dial completed")
};
let accept_fut = async {
let intro = ep_b.accept().await.expect("an introduction");
let claimed = intro.read_identity().await.expect("read_identity");
let proven = claimed.authenticate().await.expect("authenticate");
proven.accept().await.expect("accept")
};
let (ca, cb) = tokio::join!(dial_fut, accept_fut);
let echo = tokio::task::spawn_local(async move {
let bi = cb.accept_bi().await.expect("accept_bi");
let (mut tx, mut rx) = bi.split();
let mut byte = [0u8; 1];
let n = rx
.read(&mut byte)
.await
.expect("read the first byte")
.expect("a byte, not end of stream");
assert_eq!(n, 1, "the echo peer read {n} bytes, not 1");
tx.write(&byte).await.expect("echo the byte");
(tx, rx, cb)
});
let bi = ca.open_bi().await.expect("open_bi");
let (mut tx, mut rx) = bi.split();
tx.write(b"\x2a").await.expect("the first application byte");
let mut back = [0u8; 1];
let n = rx
.read(&mut back)
.await
.expect("read the echo")
.expect("an echoed byte, not end of stream");
let elapsed = t0.elapsed();
assert_eq!(n, 1, "the echo was {n} bytes, not 1");
assert_eq!(back[0], 0x2a, "the echo did not match the request");
echo.abort();
drop(tx);
drop(rx);
drop(ca);
drop(ep_a);
drop(ep_b);
elapsed
}
async fn tcp_establish_once(relay: Option<(&Relay, Duration)>) -> Duration {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let server_addr = listener.local_addr().expect("server address");
let dial = match relay {
Some((r, d)) => r.tcp(server_addr, d),
None => server_addr,
};
let echo = tokio::task::spawn_local(async move {
let (mut sock, _) = listener.accept().await.expect("accept");
sock.set_nodelay(true).expect("TCP_NODELAY on the server");
let mut byte = [0u8; 1];
sock.read_exact(&mut byte).await.expect("read one byte");
sock.write_all(&byte).await.expect("echo one byte");
sock
});
let t0 = Instant::now();
let mut client = TcpStream::connect(dial).await.expect("connect");
client.set_nodelay(true).expect("TCP_NODELAY on the client");
client.write_all(b"\x2a").await.expect("the first byte");
let mut back = [0u8; 1];
client.read_exact(&mut back).await.expect("read the echo");
let elapsed = t0.elapsed();
assert_eq!(back[0], 0x2a, "the echo did not match the request");
echo.abort();
elapsed
}
async fn slither_pingpong(relay: Option<(&Relay, Duration)>) -> Vec<f64> {
let SlitherLink {
_ep_a,
_ep_b,
ca,
cb,
..
} = slither_link(relay, WINDOWS_DEFAULT, 0x5A).await;
let echo = tokio::task::spawn_local(async move {
while let Ok(d) = cb.recv_datagram().await {
let _ = cb.send_datagram(&d);
}
});
let mut payload = [0u8; PING_PAYLOAD];
let mut out = Vec::with_capacity(PING_ITERS);
for i in 0..(PING_WARMUP + PING_ITERS) as u64 {
payload[..8].copy_from_slice(&i.to_le_bytes());
let t0 = Instant::now();
ca.send_datagram(&payload).expect("queue the datagram");
let got = tokio::time::timeout(Duration::from_secs(10), ca.recv_datagram())
.await
.unwrap_or_else(|_| panic!("no echo for datagram {i} within 10 s — the run is broken"))
.expect("the connection outlived the probe");
let dt = t0.elapsed();
assert_eq!(
&got[..8],
&i.to_le_bytes(),
"the echo did not match the request — the ping-pong lost its lockstep"
);
if i >= PING_WARMUP as u64 {
out.push(dt.as_secs_f64() * 1e6);
}
}
echo.abort();
drop(ca);
drop(_ep_a);
drop(_ep_b);
out
}
async fn tcp_pingpong(relay: Option<(&Relay, Duration)>) -> Vec<f64> {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let server_addr = listener.local_addr().expect("server address");
let dial = match relay {
Some((r, d)) => r.tcp(server_addr, d),
None => server_addr,
};
let echo = tokio::task::spawn_local(async move {
let (mut sock, _) = listener.accept().await.expect("accept");
sock.set_nodelay(true).expect("TCP_NODELAY on the server");
let mut buf = [0u8; PING_PAYLOAD];
while sock.read_exact(&mut buf).await.is_ok() {
if sock.write_all(&buf).await.is_err() {
break;
}
}
});
let mut client = TcpStream::connect(dial).await.expect("connect");
client.set_nodelay(true).expect("TCP_NODELAY on the client");
let mut payload = [0u8; PING_PAYLOAD];
let mut back = [0u8; PING_PAYLOAD];
let mut out = Vec::with_capacity(PING_ITERS);
for i in 0..(PING_WARMUP + PING_ITERS) as u64 {
payload[..8].copy_from_slice(&i.to_le_bytes());
let t0 = Instant::now();
client.write_all(&payload).await.expect("write the ping");
client.read_exact(&mut back).await.expect("read the pong");
let dt = t0.elapsed();
assert_eq!(
&back[..8],
&i.to_le_bytes(),
"the echo did not match the request"
);
if i >= PING_WARMUP as u64 {
out.push(dt.as_secs_f64() * 1e6);
}
}
echo.abort();
out
}
fn one_way(rtt_ms: u64) -> Option<Duration> {
(rtt_ms > 0).then(|| Duration::from_micros(rtt_ms * 500))
}
fn scenario_establish(relay: &Relay) {
println!("# establishment — dial to first application byte echoed back");
for (rtt_ms, samples) in [(0u64, 20usize), (20, 10), (100, 10)] {
let via = one_way(rtt_ms).map(|d| (relay, d));
let mut slither_ms = Vec::with_capacity(samples);
let mut tcp_ms = Vec::with_capacity(samples);
block_on(async {
for i in 0..samples {
let d = slither_establish_once(via, i as u8).await;
slither_ms.push(d.as_secs_f64() * 1e3);
}
for _ in 0..samples {
let d = tcp_establish_once(via).await;
tcp_ms.push(d.as_secs_f64() * 1e3);
}
});
Row {
scenario: "establish",
proto: "slither",
rtt_ms,
windows: "default",
streams: 1,
unit: "ms",
prec: 3,
samples: slither_ms,
note: None,
}
.emit();
Row {
scenario: "establish",
proto: "tcp",
rtt_ms,
windows: "n/a",
streams: 1,
unit: "ms",
prec: 3,
samples: tcp_ms,
note: (rtt_ms > 0).then(|| "proxy-terminated,missing-1-rtt".to_string()),
}
.emit();
}
println!();
}
fn scenario_bulk(relay: &Relay, rtts: &[u64], tag: &'static str) {
println!("# {tag} — one stream, steady state");
for &rtt_ms in rtts {
let via = one_way(rtt_ms).map(|d| (relay, d));
for windows in [WINDOWS_DEFAULT, WINDOWS_RAISED] {
let (samples, note) = block_on(slither_bulk(via, windows, 1, 0x30 ^ rtt_ms as u8));
Row {
scenario: tag,
proto: "slither",
rtt_ms,
windows: windows.label,
streams: 1,
unit: "MiB/s",
prec: 2,
samples,
note,
}
.emit();
}
let samples = block_on(tcp_bulk(via, false));
Row {
scenario: tag,
proto: "tcp",
rtt_ms,
windows: "kernel-default",
streams: 1,
unit: "MiB/s",
prec: 2,
samples,
note: (rtt_ms > 0).then(|| "proxy-terminated,not-a-delayed-path".to_string()),
}
.emit();
}
println!();
}
fn scenario_pingpong(relay: &Relay) {
println!("# ping-pong — 64 B, unpipelined, TCP_NODELAY on");
for rtt_ms in [0u64, 20] {
let via = one_way(rtt_ms).map(|d| (relay, d));
let samples = block_on(slither_pingpong(via));
Row {
scenario: "pingpong",
proto: "slither",
rtt_ms,
windows: "default",
streams: 1,
unit: "us",
prec: 1,
samples,
note: Some("datagram-path".into()),
}
.emit();
let samples = block_on(tcp_pingpong(via));
Row {
scenario: "pingpong",
proto: "tcp",
rtt_ms,
windows: "n/a",
streams: 1,
unit: "us",
prec: 1,
samples,
note: Some("nodelay".into()),
}
.emit();
}
println!();
}
fn scenario_mux() {
println!("# multiplexing — slither only, concurrent uni streams, clean loopback");
for windows in [WINDOWS_DEFAULT, WINDOWS_RAISED] {
for streams in MUX_STREAMS {
let (samples, note) =
block_on(slither_bulk(None, windows, streams, 0x70 ^ streams as u8));
Row {
scenario: "mux",
proto: "slither",
rtt_ms: 0,
windows: windows.label,
streams,
unit: "MiB/s",
prec: 2,
samples,
note: Some(match note {
Some(n) => format!("aggregate,{n}"),
None => "aggregate".to_string(),
}),
}
.emit();
}
}
println!();
}
fn scenario_sweep(relay: &Relay, rtt_ms: u64, tag: &'static str) {
println!("# {tag} — slither only, window ladder, one stream, rtt={rtt_ms} ms");
let via = one_way(rtt_ms).map(|d| (relay, d));
for (i, windows) in SWEEP.into_iter().enumerate() {
let (samples, note) = block_on(slither_bulk(via, windows, 1, 0xC0 ^ i as u8));
Row {
scenario: tag,
proto: "slither",
rtt_ms,
windows: windows.label,
streams: 1,
unit: "MiB/s",
prec: 2,
samples,
note,
}
.emit();
}
println!();
}
fn main() {
let which = std::env::args()
.skip(1)
.find(|a| !a.starts_with('-'))
.unwrap_or_else(|| "all".to_string());
let relay = Relay::start();
println!("bench_vs_tcp — slither versus raw kernel TCP");
println!(
" chunk={CHUNK}B ramp={BULK_RAMP:?} window={BULK_WINDOW:?} samples={BULK_SAMPLES} \
ping_iters={PING_ITERS} raised={RAISED_STREAM}/{RAISED_CONN}"
);
println!(" topology=one-thread/one-LocalSet/both-endpoints, relay=own-thread/own-runtime");
println!();
let run = |name: &str| which == "all" || which == name;
let started = Instant::now();
if run("establish") {
scenario_establish(&relay);
}
if run("bulk") {
scenario_bulk(&relay, &[0], "bulk");
}
if run("sweep") {
scenario_sweep(&relay, 0, "sweep");
}
if run("bulk-rtt") {
scenario_bulk(&relay, &[20, 50, 100], "bulk-rtt");
}
if run("sweep-rtt") {
scenario_sweep(&relay, 100, "sweep-rtt");
}
if run("pingpong") {
scenario_pingpong(&relay);
}
if run("mux") {
scenario_mux();
}
println!("BENCH_DONE wall_s={:.1}", started.elapsed().as_secs_f64());
drop(relay);
}