Skip to content

Commit 7deba4f

Browse files
authored
Add basic benchmarking (#88)
1 parent a4296c9 commit 7deba4f

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ keywords = ["mio", "epoll", "kqueue", "iocp", "wepoll"]
1414
categories = ["asynchronous", "network-programming", "os"]
1515
exclude = ["/.*"]
1616

17+
[[bench]]
18+
name = "io"
19+
harness = false
20+
1721
[dependencies]
1822
concurrent-queue = "1.2.2"
1923
futures-lite = "1.11.0"
@@ -38,6 +42,8 @@ winapi = { version = "0.3.9", features = ["winsock2"] }
3842
async-channel = "1"
3943
async-net = "1"
4044
blocking = "1"
45+
criterion = "0.3.6"
46+
getrandom = "0.2.7"
4147
signal-hook = "0.3"
4248
tempfile = "3"
4349

benches/io.rs

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//! Benchmarks for a variety of I/O operations.
2+
3+
use async_io::Async;
4+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
5+
use futures_lite::{future, prelude::*};
6+
use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream, UdpSocket};
7+
8+
/// Block on a future, either using the I/O driver or simple parking.
9+
fn block_on<R>(fut: impl Future<Output = R>, drive: bool) -> R {
10+
if drive {
11+
async_io::block_on(fut)
12+
} else {
13+
future::block_on(fut)
14+
}
15+
}
16+
17+
fn read_and_write(b: &mut Criterion) {
18+
const TCP_AMOUNT: usize = 1024 * 1024;
19+
20+
let mut group = b.benchmark_group("read_and_write");
21+
22+
for (driver_name, exec) in [("Undriven", false), ("Driven", true)] {
23+
// Benchmark the TCP streams.
24+
let init_reader_writer = || {
25+
let listener = TcpListener::bind("localhost:12345").unwrap();
26+
let read_stream = TcpStream::connect("localhost:12345").unwrap();
27+
let (write_stream, _) = listener.accept().unwrap();
28+
29+
let reader = Async::new(read_stream).unwrap();
30+
let writer = Async::new(write_stream).unwrap();
31+
32+
(listener, reader, writer)
33+
};
34+
35+
group.bench_function(format!("TcpStream.{}", driver_name), move |b| {
36+
let (_listener, mut reader, mut writer) = init_reader_writer();
37+
let mut buf = vec![0x42; TCP_AMOUNT];
38+
39+
b.iter(|| {
40+
let buf = &mut buf;
41+
42+
block_on(
43+
async {
44+
black_box(writer.write_all(&*buf).await.ok());
45+
black_box(reader.read_exact(buf).await.ok());
46+
},
47+
exec,
48+
);
49+
});
50+
});
51+
52+
#[cfg(unix)]
53+
{
54+
// Benchmark the Unix sockets.
55+
use std::os::unix::net::UnixStream;
56+
const UNIX_AMOUNT: usize = 1024;
57+
58+
group.bench_function(format!("UnixStream.{}", driver_name), |b| {
59+
let (mut reader, mut writer) = Async::<UnixStream>::pair().unwrap();
60+
let mut buf = vec![0x42; UNIX_AMOUNT];
61+
62+
b.iter(|| {
63+
let buf = &mut buf;
64+
block_on(
65+
async {
66+
black_box(writer.write_all(&*buf).await.ok());
67+
black_box(reader.read_exact(buf).await.ok());
68+
},
69+
exec,
70+
);
71+
});
72+
});
73+
}
74+
}
75+
}
76+
77+
fn connect_and_accept(c: &mut Criterion) {
78+
let mut group = c.benchmark_group("connect_and_accept");
79+
80+
for (driver_name, exec) in [("Undriven", false), ("Driven", true)] {
81+
// Benchmark the TCP streams.
82+
group.bench_function(format!("TcpStream.{}", driver_name), move |b| {
83+
let socket_addr =
84+
SocketAddr::new("127.0.0.1".parse::<Ipv4Addr>().unwrap().into(), 12345);
85+
let listener = Async::<TcpListener>::bind(socket_addr).unwrap();
86+
87+
b.iter(|| {
88+
block_on(
89+
async {
90+
let _reader = Async::<TcpStream>::connect(socket_addr).await.ok();
91+
black_box(listener.accept().await.ok());
92+
drop(black_box(_reader));
93+
},
94+
exec,
95+
);
96+
});
97+
});
98+
99+
#[cfg(unix)]
100+
{
101+
// Benchmark the Unix sockets.
102+
use std::os::unix::net::{UnixListener, UnixStream};
103+
104+
let mut id = [0u8; 8];
105+
getrandom::getrandom(&mut id).unwrap();
106+
let id = u64::from_ne_bytes(id);
107+
108+
let socket_addr = format!("/tmp/async-io-bench-{}.sock", id);
109+
let listener = Async::<UnixListener>::bind(&socket_addr).unwrap();
110+
111+
group.bench_function(format!("UnixStream.{}", driver_name), |b| {
112+
b.iter(|| {
113+
block_on(
114+
async {
115+
let _reader = Async::<UnixStream>::connect(&socket_addr).await.ok();
116+
black_box(listener.accept().await.ok());
117+
drop(black_box(_reader));
118+
},
119+
exec,
120+
);
121+
});
122+
});
123+
124+
drop(listener);
125+
}
126+
}
127+
}
128+
129+
fn udp_send_recv(c: &mut Criterion) {
130+
const UDP_AMOUNT: usize = 1024;
131+
132+
let mut group = c.benchmark_group("udp_send_recv");
133+
134+
// Create a pair of UDP sockets.
135+
let socket_addr = |port| SocketAddr::new("127.0.0.1".parse::<Ipv4Addr>().unwrap().into(), port);
136+
let socket_addr1 = socket_addr(12345);
137+
let socket_addr2 = socket_addr(12346);
138+
139+
let reader = Async::<UdpSocket>::bind(socket_addr1).unwrap();
140+
let writer = Async::<UdpSocket>::bind(socket_addr2).unwrap();
141+
142+
let mut buf = vec![0x42; UDP_AMOUNT];
143+
144+
for (driver_name, exec) in [("Undriven", false), ("Driven", true)] {
145+
group.bench_function(format!("UdpSocket.{}", driver_name), |b| {
146+
b.iter(|| {
147+
let buf = &mut buf;
148+
149+
block_on(
150+
async {
151+
black_box(writer.send_to(&*buf, socket_addr1).await.ok());
152+
black_box(reader.recv_from(buf).await.ok());
153+
},
154+
exec,
155+
);
156+
});
157+
});
158+
}
159+
}
160+
161+
criterion_group! {
162+
io_benchmarks,
163+
read_and_write,
164+
connect_and_accept,
165+
udp_send_recv
166+
}
167+
168+
criterion_main!(io_benchmarks);

0 commit comments

Comments
 (0)