Browse Source

Add the `loopback_benchmark`

Ruihan Li 9 months ago
parent
commit
bfbff600ae
3 changed files with 129 additions and 0 deletions
  1. 4 0
      Cargo.toml
  2. 24 0
      README.md
  3. 101 0
      examples/loopback_benchmark.rs

+ 4 - 0
Cargo.toml

@@ -292,6 +292,10 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac
 name = "loopback"
 required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"]
 
+[[example]]
+name = "loopback_benchmark"
+required-features = ["std", "log", "medium-ethernet", "proto-ipv4", "socket-tcp"]
+
 [[example]]
 name = "multicast"
 required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "socket-udp"]

+ 24 - 0
README.md

@@ -553,6 +553,30 @@ is possible; otherwise, nothing at all will be displayed and no options are acce
 
 [wireshark]: https://wireshark.org
 
+### examples/loopback\_benchmark.rs
+
+_examples/loopback_benchmark.rs_ is another simple throughput benchmark.
+
+Read its [source code](/examples/loopback_benchmark.rs), then run it as:
+
+```sh
+cargo run --release --example loopback_benchmark
+```
+
+It establishes a connection to itself via a loopback interface and transfers a large amount
+of data in one direction.
+
+A typical result (achieved on a Intel Core i5-13500H CPU and a Linux 6.9.9 x86_64 kernel running
+on a LENOVO XiaoXinPro 14 IRH8 laptop) is as follows:
+
+```
+$ cargo run --release --example loopback_benchmark
+done in 0.558 s, bandwidth is 15.395083 Gbps
+```
+
+Note: Although the loopback interface can be used in bare-metal environments,
+this benchmark _does_ rely on `std` to be able to measure the time cost.
+
 ## License
 
 _smoltcp_ is distributed under the terms of 0-clause BSD license.

+ 101 - 0
examples/loopback_benchmark.rs

@@ -0,0 +1,101 @@
+mod utils;
+
+use log::debug;
+
+use smoltcp::iface::{Config, Interface, SocketSet};
+use smoltcp::phy::{Device, Loopback, Medium};
+use smoltcp::socket::tcp;
+use smoltcp::time::Instant;
+use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
+
+fn main() {
+    let device = Loopback::new(Medium::Ethernet);
+
+    let mut device = {
+        utils::setup_logging("info");
+
+        let (mut opts, mut free) = utils::create_options();
+        utils::add_middleware_options(&mut opts, &mut free);
+
+        let mut matches = utils::parse_options(&opts, free);
+        utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true)
+    };
+
+    // Create interface
+    let config = match device.capabilities().medium {
+        Medium::Ethernet => {
+            Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into())
+        }
+        Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip),
+        Medium::Ieee802154 => todo!(),
+    };
+
+    let mut iface = Interface::new(config, &mut device, Instant::now());
+    iface.update_ip_addrs(|ip_addrs| {
+        ip_addrs
+            .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8))
+            .unwrap();
+    });
+
+    // Create sockets
+    let server_socket = {
+        let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 65536]);
+        let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 65536]);
+        tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer)
+    };
+
+    let client_socket = {
+        let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 65536]);
+        let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 65536]);
+        tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer)
+    };
+
+    let mut sockets: [_; 2] = Default::default();
+    let mut sockets = SocketSet::new(&mut sockets[..]);
+    let server_handle = sockets.add(server_socket);
+    let client_handle = sockets.add(client_socket);
+
+    let start_time = Instant::now();
+
+    let mut did_listen = false;
+    let mut did_connect = false;
+    let mut processed = 0;
+    while processed < 1024 * 1024 * 1024 {
+        iface.poll(Instant::now(), &mut device, &mut sockets);
+
+        let socket = sockets.get_mut::<tcp::Socket>(server_handle);
+        if !socket.is_active() && !socket.is_listening() && !did_listen {
+            debug!("listening");
+            socket.listen(1234).unwrap();
+            did_listen = true;
+        }
+
+        while socket.can_recv() {
+            let received = socket.recv(|buffer| (buffer.len(), buffer.len())).unwrap();
+            debug!("got {:?}", received,);
+            processed += received;
+        }
+
+        let socket = sockets.get_mut::<tcp::Socket>(client_handle);
+        let cx = iface.context();
+        if !socket.is_open() && !did_connect {
+            debug!("connecting");
+            socket
+                .connect(cx, (IpAddress::v4(127, 0, 0, 1), 1234), 65000)
+                .unwrap();
+            did_connect = true;
+        }
+
+        while socket.can_send() {
+            debug!("sending");
+            socket.send(|buffer| (buffer.len(), ())).unwrap();
+        }
+    }
+
+    let duration = Instant::now() - start_time;
+    println!(
+        "done in {} s, bandwidth is {} Gbps",
+        duration.total_millis() as f64 / 1000.0,
+        (processed as u64 * 8 / duration.total_millis()) as f64 / 1000000.0
+    );
+}