mirror of
https://github.com/n0-computer/noq.git
synced 2026-09-19 01:36:08 +00:00
bench(udp): measure non-GSO & GSO on localhost (#1915)
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user