//! gRPC Streaming Load Benchmark - Wave 68 Agent 4 //! //! Validates HTTP/2 streaming optimizations from Wave 67 Agent 3 under load. //! Run with: cargo bench --bench grpc_streaming_load use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput}; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; /// Stream type classification matching Wave 67 Agent 3 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StreamType { HighFrequency, // 100K buffer, target >50K msg/sec MediumFrequency, // 10K buffer, target >10K msg/sec LowFrequency, // 1K buffer, target >1K msg/sec } impl StreamType { pub fn buffer_size(&self) -> usize { match self { StreamType::HighFrequency => 100_000, StreamType::MediumFrequency => 10_000, StreamType::LowFrequency => 1_000, } } pub fn target_throughput(&self) -> u64 { match self { StreamType::HighFrequency => 50_000, // 50K msg/sec StreamType::MediumFrequency => 10_000, // 10K msg/sec StreamType::LowFrequency => 1_000, // 1K msg/sec } } pub fn name(&self) -> &'static str { match self { StreamType::HighFrequency => "HighFrequency", StreamType::MediumFrequency => "MediumFrequency", StreamType::LowFrequency => "LowFrequency", } } } /// Simulated message processing with HTTP/2 optimizations fn process_message_with_tcp_nodelay(data: &[u8], tcp_nodelay: bool) -> u64 { // Simulate network latency let base_latency_ns = 5_000; // 5μs base processing let network_latency_ns = if tcp_nodelay { 10_000 // 10μs with tcp_nodelay } else { 40_000_000 // 40ms without tcp_nodelay (Nagle's algorithm) }; // Simulate processing work let mut checksum: u64 = 0; for &byte in data { checksum = checksum.wrapping_add(byte as u64); } base_latency_ns + network_latency_ns + checksum % 1000 } /// Benchmark message throughput for different StreamTypes fn bench_stream_throughput(c: &mut Criterion) { let mut group = c.benchmark_group("grpc_streaming_throughput"); for stream_type in [ StreamType::HighFrequency, StreamType::MediumFrequency, StreamType::LowFrequency, ] { let buffer_size = stream_type.buffer_size(); let message_size = 128; // 128 bytes per message group.throughput(Throughput::Elements(buffer_size as u64)); group.bench_with_input( BenchmarkId::new("with_tcp_nodelay", stream_type.name()), &stream_type, |b, &st| { let messages: Vec> = (0..st.buffer_size()) .map(|i| vec![i as u8; message_size]) .collect(); b.iter(|| { for msg in &messages { black_box(process_message_with_tcp_nodelay(msg, true)); } }); }, ); group.bench_with_input( BenchmarkId::new("without_tcp_nodelay", stream_type.name()), &stream_type, |b, &st| { let messages: Vec> = (0..st.buffer_size()) .map(|i| vec![i as u8; message_size]) .collect(); b.iter(|| { for msg in &messages { black_box(process_message_with_tcp_nodelay(msg, false)); } }); }, ); } group.finish(); } /// Benchmark HTTP/2 window sizing impact fn bench_http2_window_sizing(c: &mut Criterion) { let mut group = c.benchmark_group("http2_window_sizing"); let window_sizes = [ ("1MB", 1024 * 1024), ("2MB", 2 * 1024 * 1024), ("5MB", 5 * 1024 * 1024), ("10MB", 10 * 1024 * 1024), ]; for (name, window_size) in window_sizes { group.bench_with_input( BenchmarkId::from_parameter(name), &window_size, |b, &ws| { // Simulate flow control operations let counter = Arc::new(AtomicU64::new(0)); b.iter(|| { let mut bytes_sent = 0u64; while bytes_sent < ws { bytes_sent += 1024; // Send 1KB chunks counter.fetch_add(1, Ordering::Relaxed); // Simulate window update check if bytes_sent % (ws / 10) == 0 { black_box(counter.load(Ordering::Relaxed)); } } }); }, ); } group.finish(); } /// Benchmark backpressure handling fn bench_backpressure_handling(c: &mut Criterion) { let mut group = c.benchmark_group("backpressure_handling"); for stream_type in [ StreamType::HighFrequency, StreamType::MediumFrequency, ] { let buffer_size = stream_type.buffer_size(); group.bench_with_input( BenchmarkId::from_parameter(stream_type.name()), &stream_type, |b, &st| { let buffer_capacity = st.buffer_size(); b.iter(|| { let mut buffer = Vec::with_capacity(buffer_capacity); let mut backpressure_events = 0u64; // Simulate message arrival for i in 0..(buffer_capacity * 2) { if buffer.len() >= buffer_capacity { // Backpressure activated backpressure_events += 1; buffer.clear(); // Simulate drain } buffer.push(i); } black_box(backpressure_events); }); }, ); } group.finish(); } /// Benchmark latency percentile calculations fn bench_latency_percentiles(c: &mut Criterion) { let mut group = c.benchmark_group("latency_percentiles"); let sample_sizes = [1_000, 10_000, 100_000]; for &sample_size in &sample_sizes { group.bench_with_input( BenchmarkId::from_parameter(sample_size), &sample_size, |b, &size| { let mut samples: Vec = (0..size) .map(|i| (i * 1000 + i % 100) as u64) .collect(); b.iter(|| { samples.sort_unstable(); // Calculate percentiles let p50_idx = (size * 50 / 100).min(size - 1); let p95_idx = (size * 95 / 100).min(size - 1); let p99_idx = (size * 99 / 100).min(size - 1); let p50 = samples[p50_idx]; let p95 = samples[p95_idx]; let p99 = samples[p99_idx]; black_box((p50, p95, p99)); }); }, ); } group.finish(); } criterion_group!( benches, bench_stream_throughput, bench_http2_window_sizing, bench_backpressure_handling, bench_latency_percentiles ); criterion_main!(benches);