Skip to content

Commit

Permalink
bench(udp): measure non-GSO & GSO on localhost (#1915)
Browse files Browse the repository at this point in the history
This commit adds a basic benchmark, measuring the time to transfer 10
MiB with and without GSO on localhost.

```
gso_false/throughput    time:   [23.455 ms 25.230 ms 27.004 ms]
                        thrpt:  [370.31 MiB/s 396.35 MiB/s 426.35 MiB/s]

gso_true/throughput     time:   [1.0168 ms 1.1239 ms 1.2496 ms]
                        thrpt:  [7.8153 GiB/s 8.6889 GiB/s 9.6044 GiB/s]
```

```
$ cat /proc/cpuinfo
model name      : 12th Gen Intel(R) Core(TM) i9-12900HK
```

---

Given various optimizations for `localhost` traffic on Linux, we were
wondering whether GSO actually has an impact on `localhost`. According
to this benchmark: Yes it does.

For what a `localhost` network benchmark is worth, here it is. Feel free
to close in case it isn't useful.
  • Loading branch information
mxinden committed Jul 8, 2024
1 parent 693c9b7 commit 36407fe
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
7 changes: 7 additions & 0 deletions quinn-udp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@ tracing = { workspace = true }
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_System_IO", "Win32_Networking_WinSock"] }
once_cell = "1.19.0"

[dev-dependencies]
criterion = "0.5"

[target.'cfg(any(target_os = "linux", target_os = "windows"))'.bench]
name = "throughput"
harness = false
75 changes: 75 additions & 0 deletions quinn-udp/benches/throughput.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use criterion::{criterion_group, criterion_main, Criterion};
use quinn_udp::{RecvMeta, Transmit, UdpSocketState};
use std::cmp::min;
use std::{io::IoSliceMut, net::UdpSocket, slice};

pub fn criterion_benchmark(c: &mut Criterion) {
const TOTAL_BYTES: usize = 10 * 1024 * 1024;
// Maximum GSO buffer size is 64k.
const MAX_BUFFER_SIZE: usize = u16::MAX as usize;
const SEGMENT_SIZE: usize = 1280;

let send = UdpSocket::bind("[::1]:0")
.or_else(|_| UdpSocket::bind("127.0.0.1:0"))
.unwrap();
let recv = UdpSocket::bind("[::1]:0")
.or_else(|_| UdpSocket::bind("127.0.0.1:0"))
.unwrap();
let max_segments = min(
UdpSocketState::new((&send).into())
.unwrap()
.max_gso_segments(),
MAX_BUFFER_SIZE / SEGMENT_SIZE,
);
let dst_addr = recv.local_addr().unwrap();
let send_state = UdpSocketState::new((&send).into()).unwrap();
let recv_state = UdpSocketState::new((&recv).into()).unwrap();
// Reverse non-blocking flag set by `UdpSocketState` to make the test non-racy
recv.set_nonblocking(false).unwrap();

let mut receive_buffer = vec![0; MAX_BUFFER_SIZE];
let mut meta = RecvMeta::default();

for gso_enabled in [false, true] {
let mut group = c.benchmark_group(format!("gso_{}", gso_enabled));
group.throughput(criterion::Throughput::Bytes(TOTAL_BYTES as u64));

let segments = if gso_enabled { max_segments } else { 1 };
let msg = vec![0xAB; SEGMENT_SIZE * segments];

let transmit = Transmit {
destination: dst_addr,
ecn: None,
contents: &msg,
segment_size: gso_enabled.then_some(SEGMENT_SIZE),
src_ip: None,
};

group.bench_function("throughput", |b| {
b.iter(|| {
let mut sent: usize = 0;
while sent < TOTAL_BYTES {
send_state.send((&send).into(), &transmit).unwrap();
sent += transmit.contents.len();

let mut received_segments = 0;
while received_segments < segments {
let n = recv_state
.recv(
(&recv).into(),
&mut [IoSliceMut::new(&mut receive_buffer)],
slice::from_mut(&mut meta),
)
.unwrap();
assert_eq!(n, 1);
received_segments += meta.len / meta.stride;
}
assert_eq!(received_segments, segments);
}
})
});
}
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

0 comments on commit 36407fe

Please sign in to comment.