use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use flowparser_sflow::SflowParser;
fn build_realistic_datagram() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&0u32.to_be_bytes()); data.extend_from_slice(&5u32.to_be_bytes()); data.extend_from_slice(&1u32.to_be_bytes()); data.extend_from_slice(&[10, 0, 0, 1]); data.extend_from_slice(&0u32.to_be_bytes()); data.extend_from_slice(&42u32.to_be_bytes()); data.extend_from_slice(&1000u32.to_be_bytes());
let num_samples: u32 = 8;
data.extend_from_slice(&num_samples.to_be_bytes());
for seq in 0..num_samples {
let sample = build_flow_sample(seq + 1);
data.extend_from_slice(&1u32.to_be_bytes());
data.extend_from_slice(&(sample.len() as u32).to_be_bytes());
data.extend(sample);
}
data[0..4].copy_from_slice(&5u32.to_be_bytes());
let proper = data[4..].to_vec();
let mut result = Vec::new();
result.extend_from_slice(&5u32.to_be_bytes()); result.extend_from_slice(&proper[4..]); build_clean_datagram(num_samples)
}
fn build_clean_datagram(num_samples: u32) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&5u32.to_be_bytes()); data.extend_from_slice(&1u32.to_be_bytes()); data.extend_from_slice(&[10, 0, 0, 1]); data.extend_from_slice(&0u32.to_be_bytes()); data.extend_from_slice(&42u32.to_be_bytes()); data.extend_from_slice(&1000u32.to_be_bytes()); data.extend_from_slice(&num_samples.to_be_bytes());
for seq in 0..num_samples {
let sample = build_flow_sample(seq + 1);
data.extend_from_slice(&1u32.to_be_bytes()); data.extend_from_slice(&(sample.len() as u32).to_be_bytes());
data.extend(&sample);
}
data
}
fn build_flow_sample(seq: u32) -> Vec<u8> {
let mut records = Vec::new();
{
let header_bytes = [0xAAu8; 64]; let mut rec = Vec::new();
rec.extend_from_slice(&1u32.to_be_bytes()); rec.extend_from_slice(&128u32.to_be_bytes()); rec.extend_from_slice(&0u32.to_be_bytes()); rec.extend_from_slice(&(header_bytes.len() as u32).to_be_bytes());
rec.extend_from_slice(&header_bytes);
records.extend_from_slice(&1u32.to_be_bytes()); records.extend_from_slice(&(rec.len() as u32).to_be_bytes());
records.extend(rec);
}
{
let mut rec = Vec::new();
rec.extend_from_slice(&100u32.to_be_bytes()); rec.extend_from_slice(&0u32.to_be_bytes()); rec.extend_from_slice(&200u32.to_be_bytes()); rec.extend_from_slice(&0u32.to_be_bytes()); records.extend_from_slice(&((0 << 12) | 1001u32).to_be_bytes());
records.extend_from_slice(&(rec.len() as u32).to_be_bytes());
records.extend(rec);
}
{
let mut rec = Vec::new();
rec.extend_from_slice(&1u32.to_be_bytes()); rec.extend_from_slice(&[192, 168, 1, 1]); rec.extend_from_slice(&24u32.to_be_bytes()); rec.extend_from_slice(&24u32.to_be_bytes()); records.extend_from_slice(&((0 << 12) | 1002u32).to_be_bytes());
records.extend_from_slice(&(rec.len() as u32).to_be_bytes());
records.extend(rec);
}
{
let mut rec = Vec::new();
rec.extend_from_slice(&2u32.to_be_bytes()); rec.extend_from_slice(&1460u32.to_be_bytes()); rec.extend_from_slice(&1460u32.to_be_bytes()); rec.extend_from_slice(&5u32.to_be_bytes()); rec.extend_from_slice(&0u32.to_be_bytes()); rec.extend_from_slice(&0u32.to_be_bytes()); rec.extend_from_slice(&1500u32.to_be_bytes()); rec.extend_from_slice(&10000u32.to_be_bytes()); rec.extend_from_slice(&5000u32.to_be_bytes()); rec.extend_from_slice(&65535u32.to_be_bytes()); rec.extend_from_slice(&3u32.to_be_bytes()); rec.extend_from_slice(&8000u32.to_be_bytes()); records.extend_from_slice(&((0 << 12) | 2209u32).to_be_bytes());
records.extend_from_slice(&(rec.len() as u32).to_be_bytes());
records.extend(rec);
}
let num_records: u32 = 4;
let mut sample = Vec::new();
sample.extend_from_slice(&seq.to_be_bytes()); sample.extend_from_slice(&3u32.to_be_bytes()); sample.extend_from_slice(&256u32.to_be_bytes()); sample.extend_from_slice(&10000u32.to_be_bytes()); sample.extend_from_slice(&0u32.to_be_bytes()); sample.extend_from_slice(&1u32.to_be_bytes()); sample.extend_from_slice(&2u32.to_be_bytes()); sample.extend_from_slice(&num_records.to_be_bytes());
sample.extend(records);
sample
}
fn bench_throughput(c: &mut Criterion) {
let datagram = build_realistic_datagram();
let parser = SflowParser::default();
let result = parser.parse_bytes(&datagram);
assert!(
result.error.is_none(),
"Test datagram should parse without error"
);
assert_eq!(result.datagrams.len(), 1);
assert_eq!(result.datagrams[0].samples.len(), 8);
let mut group = c.benchmark_group("throughput");
group.throughput(Throughput::Bytes(datagram.len() as u64));
group.bench_with_input(
BenchmarkId::new("single_datagram", datagram.len()),
&datagram,
|b, data| {
b.iter(|| parser.parse_bytes(data));
},
);
let batch_count = 100;
let batch: Vec<u8> = std::iter::repeat_n(&datagram, batch_count)
.flat_map(|d| d.iter().copied())
.collect();
group.throughput(Throughput::Bytes(batch.len() as u64));
group.bench_with_input(
BenchmarkId::new("batch_100_datagrams", batch.len()),
&batch,
|b, data| {
b.iter(|| parser.parse_bytes(data));
},
);
group.finish();
}
criterion_group!(benches, bench_throughput);
criterion_main!(benches);