diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index ed003b1f..837fa033 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -32,6 +32,10 @@ path = "src/lib.rs" name = "tx_throughput" harness = false +[[bench]] +name = "packet_bytes_extraction" +harness = false + [dependencies] git-version = "0.3.9" diff --git a/easytier/benches/README.md b/easytier/benches/README.md index 29f750ee..a3aadbfd 100644 --- a/easytier/benches/README.md +++ b/easytier/benches/README.md @@ -1,4 +1,47 @@ -# TX Throughput Benchmark +# Benchmarks + +Criterion benchmarks for EasyTier hot paths. + +| Bench | What it measures | +| --------------------------- | -------------------------------------------------------------------------------- | +| `tx_throughput` | End-to-end TX injection path through `peer_manager::send_msg_by_ip` | +| `packet_bytes_extraction` | `ZCPacket::payload_bytes` / `tunnel_payload_bytes` extraction (advance hot path) | + +## Packet Bytes Extraction + +Criterion benchmark for `ZCPacket` bytes extraction — the methods touched by the +`advance`-based slicing refactor. Measures `payload_bytes` and +`tunnel_payload_bytes` at two payload sizes (1280, 4096). Setup +(`ZCPacket::new_with_payload`) runs in the benchmark harness's preparation +phase and is excluded from the timed region, so the numbers reflect only the +extraction call. + +### Quick start + +```bash +cargo bench --bench packet_bytes_extraction +``` + +Smoke run: + +```bash +PACKET_BYTES_MEASUREMENT_SECS=2 \ +PACKET_BYTES_WARMUP_SECS=1 \ +PACKET_BYTES_SAMPLE_SIZE=10 \ +cargo bench --bench packet_bytes_extraction -- --quiet +``` + +### Environment variables + +| Variable | Default | Notes | +| ------------------------------- | ------- | ---------------------------- | +| `PACKET_BYTES_MEASUREMENT_SECS` | `10` | Criterion `measurement_time` | +| `PACKET_BYTES_WARMUP_SECS` | `3` | Criterion `warm_up_time` | +| `PACKET_BYTES_SAMPLE_SIZE` | `10` | Criterion `sample_size` (min 10) | + +--- + +## TX Throughput Benchmark Criterion benchmark for EasyTier's TX injection path (`peer_manager::send_msg_by_ip`). diff --git a/easytier/benches/packet_bytes_extraction.rs b/easytier/benches/packet_bytes_extraction.rs new file mode 100644 index 00000000..1ff2ecbd --- /dev/null +++ b/easytier/benches/packet_bytes_extraction.rs @@ -0,0 +1,65 @@ +use std::hint::black_box; +use std::time::Duration; + +use criterion::{BatchSize, Criterion, Throughput, criterion_group, criterion_main}; + +use easytier::tunnel::packet_def::ZCPacket; + +const PAYLOAD_SIZES: &[usize] = &[1280, 4096]; + +fn env_parse(key: &str, default: T) -> T { + std::env::var(key) + .ok() + .and_then(|v| v.parse().ok()) + .unwrap_or(default) +} + +fn bench_payload_bytes(c: &mut Criterion) { + let mut group = c.benchmark_group("payload_bytes"); + for &size in PAYLOAD_SIZES { + let data = vec![0u8; size]; + group.throughput(Throughput::Bytes(size as u64)); + group.bench_with_input(format!("{size}"), &data, |b, data| { + b.iter_batched( + || ZCPacket::new_with_payload(black_box(data)), + |p| black_box(p).payload_bytes(), + BatchSize::SmallInput, + ) + }); + } + group.finish(); +} + +fn bench_tunnel_payload_bytes(c: &mut Criterion) { + let mut group = c.benchmark_group("tunnel_payload_bytes"); + for &size in PAYLOAD_SIZES { + let data = vec![0u8; size]; + group.throughput(Throughput::Bytes(size as u64)); + group.bench_with_input(format!("{size}"), &data, |b, data| { + b.iter_batched( + || ZCPacket::new_with_payload(black_box(data)), + |p| black_box(p).tunnel_payload_bytes(), + BatchSize::SmallInput, + ) + }); + } + group.finish(); +} + +fn criterion_config() -> Criterion { + let measurement_secs = env_parse("PACKET_BYTES_MEASUREMENT_SECS", 10u64); + let warmup_secs = env_parse("PACKET_BYTES_WARMUP_SECS", 3u64); + let sample_size = env_parse("PACKET_BYTES_SAMPLE_SIZE", 10usize).max(10); + + Criterion::default() + .measurement_time(Duration::from_secs(measurement_secs)) + .warm_up_time(Duration::from_secs(warmup_secs)) + .sample_size(sample_size) +} + +criterion_group! { + name = benches; + config = criterion_config(); + targets = bench_payload_bytes, bench_tunnel_payload_bytes +} +criterion_main!(benches); diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index 743c507f..2ff52af4 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -24,7 +24,7 @@ use crate::{ }; use byteorder::WriteBytesExt as _; -use bytes::{BufMut, BytesMut}; +use bytes::{Buf, BufMut, BytesMut}; use cidr::{Ipv4Inet, Ipv6Inet}; use futures::{SinkExt, Stream, StreamExt, lock::BiLock, ready}; use pin_project_lite::pin_project; @@ -180,12 +180,13 @@ impl ZCPacketToBytes for TunZCPacketToBytes { assert!(payload_offset >= 4); let ret = if self.has_packet_info { - let mut inner = inner.split_off(payload_offset - 4); + inner.advance(payload_offset - 4); let proto = infer_proto(&inner[4..]); self.fill_packet_info(&mut inner[0..4], proto)?; inner } else { - inner.split_off(payload_offset) + inner.advance(payload_offset); + inner }; tracing::debug!(?ret, ?payload_offset, "convert zc packet to tun packet"); diff --git a/easytier/src/tunnel/packet_def.rs b/easytier/src/tunnel/packet_def.rs index 4ecc64c7..b659cf4f 100644 --- a/easytier/src/tunnel/packet_def.rs +++ b/easytier/src/tunnel/packet_def.rs @@ -1,3 +1,4 @@ +use bytes::Buf; use bytes::Bytes; use bytes::BytesMut; use zerocopy::AsBytes; @@ -486,7 +487,7 @@ impl ZCPacket { let total_len = payload_off + payload.len(); ret.inner.reserve(total_len); unsafe { ret.inner.set_len(total_len) }; - ret.mut_payload()[..payload.len()].copy_from_slice(payload); + ret.mut_payload().copy_from_slice(payload); ret } @@ -587,7 +588,8 @@ impl ZCPacket { } pub fn payload_bytes(mut self) -> BytesMut { - self.inner.split_off(self.payload_offset()) + self.inner.advance(self.payload_offset()); + self.inner } pub fn peer_manager_header(&self) -> Option<&PeerManagerHeader> { @@ -652,11 +654,12 @@ impl ZCPacket { } pub fn tunnel_payload_bytes(mut self) -> BytesMut { - self.inner.split_off( + self.inner.advance( self.packet_type .get_packet_offsets() .peer_manager_header_offset, - ) + ); + self.inner } pub fn convert_type(mut self, target_packet_type: ZCPacketType) -> Self { @@ -702,7 +705,8 @@ impl ZCPacket { return Self::new_from_buf(buf, target_packet_type); } - Self::new_from_buf(self.inner.split_off(new_offset), target_packet_type) + self.inner.advance(new_offset); + Self::new_from_buf(self.inner, target_packet_type) } pub fn into_bytes(self) -> Bytes { @@ -748,8 +752,10 @@ impl ZCPacket { let foreign_hdr_len = hdr.get_header_len(); Self::new_from_buf( - self.inner - .split_off(foreign_hdr_len + self.payload_offset()), + { + self.inner.advance(foreign_hdr_len + self.payload_offset()); + self.inner + }, ZCPacketType::DummyTunnel, ) }