//! gRPC Streaming Throughput Benchmarks //! //! Validates streaming performance targets: //! - Message throughput: >10,000 msg/sec //! - Stream latency: p99 <1ms //! - Backpressure handling: graceful degradation //! - Concurrent stream capacity: >100 streams //! //! Critical for real-time market data and order flow. use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::sync::{ atomic::{AtomicU64, Ordering}, Arc, }; use std::time::Duration; /// Mock streaming message #[derive(Clone, Debug)] struct StreamMessage { payload: Vec, } impl StreamMessage { fn new(_sequence: u64, payload_size: usize) -> Self { Self { payload: vec![0u8; payload_size], } } } /// Mock streaming channel struct StreamChannel { buffer: Vec, capacity: usize, sent_count: Arc, } impl StreamChannel { fn new(capacity: usize) -> Self { Self { buffer: Vec::with_capacity(capacity), capacity, sent_count: Arc::new(AtomicU64::new(0)), } } fn send(&mut self, message: StreamMessage) -> Result<(), &'static str> { if self.buffer.len() < self.capacity { self.buffer.push(message); self.sent_count.fetch_add(1, Ordering::Relaxed); Ok(()) } else { Err("Channel full") } } fn receive(&mut self) -> Option { if !self.buffer.is_empty() { Some(self.buffer.remove(0)) } else { None } } } /// Benchmark message throughput fn bench_message_throughput(c: &mut Criterion) { let mut group = c.benchmark_group("message_throughput"); for payload_size in &[64, 256, 1024, 4096] { group.throughput(Throughput::Bytes(*payload_size as u64)); group.bench_with_input( BenchmarkId::new("payload_bytes", payload_size), payload_size, |b, &size| { b.iter_batched( || StreamChannel::new(10000), |mut channel| { for i in 0..1000 { let msg = StreamMessage::new(i, size); let _ = channel.send(msg); } black_box(channel) }, criterion::BatchSize::SmallInput, ); }, ); } group.finish(); } /// Benchmark stream latency (send to receive) fn bench_stream_latency(c: &mut Criterion) { let mut group = c.benchmark_group("stream_latency"); group.throughput(Throughput::Elements(1)); group.bench_function("send_receive_latency", |b| { b.iter_batched( || StreamChannel::new(1000), |mut channel| { let msg = StreamMessage::new(1, 256); let _ = channel.send(msg); let received = channel.receive(); black_box(received) }, criterion::BatchSize::SmallInput, ); }); group.finish(); } /// Benchmark backpressure handling fn bench_backpressure(c: &mut Criterion) { let mut group = c.benchmark_group("backpressure_handling"); for buffer_size in &[100, 1000, 10000] { group.bench_with_input( BenchmarkId::new("buffer_size", buffer_size), buffer_size, |b, &size| { b.iter_batched( || StreamChannel::new(size), |mut channel| { let mut successful = 0; let mut failed = 0; // Try to send more than capacity for i in 0..(size * 2) { let msg = StreamMessage::new(i as u64, 256); match channel.send(msg) { Ok(_) => successful += 1, Err(_) => failed += 1, } } black_box((successful, failed, channel)) }, criterion::BatchSize::SmallInput, ); }, ); } group.finish(); } /// Benchmark concurrent streams fn bench_concurrent_streams(c: &mut Criterion) { let mut group = c.benchmark_group("concurrent_streams"); for num_streams in &[10, 50, 100, 200] { group.bench_with_input( BenchmarkId::new("streams", num_streams), num_streams, |b, &streams| { b.iter(|| { let mut channels: Vec = Vec::new(); // Create multiple streams for _ in 0..streams { channels.push(StreamChannel::new(1000)); } // Send messages to all streams for channel in &mut channels { for i in 0..10 { let msg = StreamMessage::new(i, 256); let _ = channel.send(msg); } } black_box(channels) }); }, ); } group.finish(); } /// Benchmark message serialization overhead fn bench_serialization(c: &mut Criterion) { let mut group = c.benchmark_group("message_serialization"); for payload_size in &[64, 256, 1024] { group.throughput(Throughput::Bytes(*payload_size as u64)); group.bench_with_input( BenchmarkId::new("bytes", payload_size), payload_size, |b, &size| { b.iter(|| { let msg = StreamMessage::new(1, size); // Simulate serialization (copy payload) let serialized = msg.payload.clone(); black_box(serialized) }); }, ); } group.finish(); } /// Benchmark flow control fn bench_flow_control(c: &mut Criterion) { let mut group = c.benchmark_group("flow_control"); group.bench_function("windowed_send", |b| { b.iter(|| { let mut channel = StreamChannel::new(1000); let window_size = 100; // Send in windows with flow control for window in 0..10 { // Send window for i in 0..window_size { let msg = StreamMessage::new(window * window_size + i, 256); let _ = channel.send(msg); } // Receive window (simulate acknowledgment) for _ in 0..window_size { let _ = channel.receive(); } } black_box(channel) }); }); group.bench_function("continuous_send", |b| { b.iter(|| { let mut channel = StreamChannel::new(1000); // Send continuously for i in 0..1000 { let msg = StreamMessage::new(i, 256); let _ = channel.send(msg); } black_box(channel) }); }); group.finish(); } criterion_group! { name = streaming_benchmarks; config = Criterion::default() .measurement_time(Duration::from_secs(10)) .sample_size(500) .warm_up_time(Duration::from_secs(2)) .with_plots(); targets = bench_message_throughput, bench_stream_latency, bench_backpressure, bench_concurrent_streams, bench_serialization, bench_flow_control } criterion_main!(streaming_benchmarks); #[cfg(test)] mod throughput_validation { #[allow(unused_imports)] use super::*; #[allow(unused_imports)] use std::time::{Duration, Instant}; #[test] fn validate_message_throughput() { let mut channel = StreamChannel::new(100000); let message_count = 100000; let start = Instant::now(); for i in 0..message_count { let msg = StreamMessage::new(i, 256); channel.send(msg).unwrap(); } let elapsed = start.elapsed(); let messages_per_sec = (message_count as f64 / elapsed.as_secs_f64()) as u64; println!("Throughput: {} msg/sec", messages_per_sec); // Target: >10,000 msg/sec assert!( messages_per_sec > 10000, "Throughput below target: {} msg/sec (target: >10,000)", messages_per_sec ); } #[test] fn validate_stream_latency() { let mut channel = StreamChannel::new(1000); let iterations = 10000; let start = Instant::now(); for i in 0..iterations { let msg = StreamMessage::new(i, 256); channel.send(msg).unwrap(); let _ = channel.receive(); } let elapsed = start.elapsed(); let avg_latency_us = elapsed.as_micros() / iterations; println!("Average stream latency: {}us", avg_latency_us); // Target: p99 <1ms = 1000us assert!( avg_latency_us < 1000, "Stream latency exceeds 1ms target: {}us", avg_latency_us ); } #[test] fn validate_backpressure_handling() { let capacity = 1000; let mut channel = StreamChannel::new(capacity); let mut successful = 0; let mut failed = 0; // Attempt to send 2x capacity for i in 0..(capacity * 2) { let msg = StreamMessage::new(i as u64, 256); match channel.send(msg) { Ok(_) => successful += 1, Err(_) => failed += 1, } } assert_eq!( successful, capacity, "Should accept exactly capacity messages" ); assert_eq!(failed, capacity, "Should reject messages beyond capacity"); println!( "Backpressure: accepted {}, rejected {} (capacity: {})", successful, failed, capacity ); } #[test] fn validate_concurrent_streams() { let num_streams = 100; let msgs_per_stream = 100; let start = Instant::now(); let mut channels: Vec = Vec::new(); for _ in 0..num_streams { let mut channel = StreamChannel::new(1000); for i in 0..msgs_per_stream { let msg = StreamMessage::new(i, 256); channel.send(msg).unwrap(); } channels.push(channel); } let elapsed = start.elapsed(); let total_messages = num_streams * msgs_per_stream; println!( "{} streams, {} total messages in {:?}", num_streams, total_messages, elapsed ); assert!( elapsed < Duration::from_secs(1), "Concurrent stream creation too slow: {:?}", elapsed ); } }