diff --git a/Cargo.toml b/Cargo.toml index 0191491..23e49c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,10 +69,8 @@ required-features = ["std", "phy-tap_interface", "socket-tcp", "socket-udp"] name = "loopback" required-features = ["log", "socket-tcp"] -# This is really a test, but it requires root privileges for setup (the tap interface) -# so it is built as an example. [[example]] -name = "stress" +name = "benchmark" required-features = ["std", "phy-tap_interface", "socket-tcp"] [profile.release] diff --git a/README.md b/README.md index dd409c1..181af88 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], and compiles on stable Rust 1.20 and later. +_smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against +the Linux TCP stack in loopback mode. + [docs]: https://docs.rs/smoltcp/ ## Features @@ -295,6 +298,29 @@ cargo run --example client -- tap0 ADDRESS PORT It connects to the given address (not a hostname) and port (e.g. `socat stdio tcp4-listen:1234`), and will respond with reversed chunks of the input indefinitely. +### examples/benchmark.rs + +_examples/benchmark.rs_ implements a simple throughput benchmark. + +Read its [source code](/examples/benchmark.rs), then run it as: + +```sh +cargo run --release --example benchmark -- tap0 [reader|writer] +``` + +It establishes a connection to itself from a different thread and reads or writes a large amount +of data in one direction. + +A typical result (achieved on a Intel Core i7-7500U CPU and a Linux 4.9.65 x86_64 kernel running +on a Dell XPS 13 9360 laptop) is as follows: + +``` +$ cargo run -q --release --example benchmark tap0 reader +throughput: 2.219 Gbps +$ cargo run -q --release --example benchmark tap0 writer +throughput: 4.519 Gbps +``` + ## Bare-metal usage examples Examples that use no services from the host OS are necessarily less illustrative than examples diff --git a/examples/stress.rs b/examples/benchmark.rs similarity index 91% rename from examples/stress.rs rename to examples/benchmark.rs index e031ec0..626a2db 100644 --- a/examples/stress.rs +++ b/examples/benchmark.rs @@ -22,14 +22,16 @@ use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; use smoltcp::socket::SocketSet; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; -const AMOUNT: usize = 10_000_000; +const AMOUNT: usize = 1_000_000_000; enum Client { Reader, Writer } fn client(kind: Client) { let port = match kind { Client::Reader => 1234, Client::Writer => 1235 }; let mut stream = TcpStream::connect(("192.168.69.1", port)).unwrap(); - let mut buffer = vec![0; 64]; + let mut buffer = vec![0; 1_000_000]; + + let start = Instant::now(); let mut processed = 0; while processed < AMOUNT { @@ -47,7 +49,15 @@ fn client(kind: Client) { Err(err) => panic!("cannot process: {}", err) } } - println!("client done"); + + let end = Instant::now(); + + let elapsed = end - start; + let elapsed = elapsed.as_secs() as f64 + + elapsed.subsec_nanos() as f64 * 1e-9; + + println!("throughput: {:.3} Gbps", AMOUNT as f64 / elapsed / 0.125e9); + CLIENT_DONE.store(true, Ordering::SeqCst); } @@ -139,6 +149,6 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(&sockets, timestamp).or(Some(1000))).expect("wait error"); } }