//! Comprehensive HFT Performance Benchmarks //! //! This module provides exhaustive performance benchmarking for all latency-critical //! paths in the Foxhunt HFT system. Benchmarks target sub-microsecond operations //! and validate HFT performance requirements. //! //! Performance Targets: //! - Order validation: < 1μs //! - Risk calculation: < 5μs //! - Market data processing: < 100ns //! - PnL calculation: < 50ns //! - Position updates: < 2μs //! - Message serialization: < 500ns //! - Event publishing: < 1μs //! - Database writes: < 10μs use criterion::{ black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, measurement::WallTime, BatchSize }; use std::collections::{HashMap, BTreeMap}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::sync::{Arc, Mutex, atomic::{AtomicU64, Ordering}}; use parking_lot::RwLock; use crossbeam::queue::SegQueue; use serde::{Deserialize, Serialize}; // CANONICAL TYPE IMPORTS - Use types::prelude::Decimal use uuid::Uuid; use chrono::{DateTime, Utc}; // ===== PERFORMANCE-CRITICAL DATA STRUCTURES ===== /// High-performance order structure optimized for HFT #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HFTOrder { pub id: u64, pub symbol: [u8; 8], // Fixed-size symbol for better cache performance pub side: OrderSide, pub quantity: u64, // Using integers for exact arithmetic pub price: u64, // Price in ticks (e.g., cents) pub timestamp_ns: u64, // Nanosecond timestamp pub strategy_id: u16, } // OrderSide now imported from canonical source use common::OrderSide; /// High-performance market data tick #[derive(Debug, Clone, Copy)] #[repr(C)] // Ensure memory layout for SIMD operations pub struct MarketTick { pub symbol_id: u32, pub bid: u64, pub ask: u64, pub bid_size: u32, pub ask_size: u32, pub last: u64, pub volume: u32, pub timestamp_ns: u64, } /// High-performance position tracking #[derive(Debug, Clone)] pub struct PositionManager { positions: Arc>>, // symbol_id -> quantity pnl: Arc, // Atomic for lock-free updates update_count: Arc, } impl PositionManager { pub fn new() -> Self { Self { positions: Arc::new(RwLock::new(HashMap::new())), pnl: Arc::new(AtomicU64::new(0)), update_count: Arc::new(AtomicU64::new(0)), } } pub fn update_position(&self, symbol_id: u32, quantity_delta: i64) { let mut positions = self.positions.write(); *positions.entry(symbol_id).or_insert(0) += quantity_delta; self.update_count.fetch_add(1, Ordering::Relaxed); } pub fn get_position(&self, symbol_id: u32) -> i64 { self.positions.read().get(&symbol_id).copied().unwrap_or(0) } pub fn calculate_pnl(&self, symbol_id: u32, current_price: u64, entry_price: u64) -> i64 { let position = self.get_position(symbol_id); (current_price as i64 - entry_price as i64) * position } } /// High-performance risk calculator #[derive(Debug)] pub struct RiskCalculator { limits: RiskLimits, } #[derive(Debug, Clone)] pub struct RiskLimits { pub max_position: i64, pub max_order_size: u64, pub max_notional: u64, pub max_leverage: f32, } impl RiskCalculator { pub fn new() -> Self { Self { limits: RiskLimits { max_position: 10000, max_order_size: 1000, max_notional: 1000000, max_leverage: 3.0, } } } pub fn validate_order(&self, order: &HFTOrder, current_position: i64) -> bool { // Fast validation checks if order.quantity > self.limits.max_order_size { return false; } let new_position = match order.side { OrderSide::Buy => current_position + order.quantity as i64, OrderSide::Sell => current_position - order.quantity as i64, }; new_position.abs() <= self.limits.max_position } pub fn calculate_var(&self, positions: &[(u32, i64, u64)], confidence: f32) -> u64 { // Simplified VaR calculation for benchmarking let mut total_risk = 0u64; for (_, quantity, price) in positions { let notional = quantity.abs() as u64 * price; total_risk += (notional as f32 * confidence) as u64; } total_risk } } /// High-performance order book #[derive(Debug)] pub struct OrderBook { bids: BTreeMap, // price -> quantity asks: BTreeMap, last_update_ns: AtomicU64, } impl OrderBook { pub fn new() -> Self { Self { bids: BTreeMap::new(), asks: BTreeMap::new(), last_update_ns: AtomicU64::new(0), } } pub fn update_bid(&mut self, price: u64, quantity: u64) { if quantity == 0 { self.bids.remove(&price); } else { self.bids.insert(price, quantity); } self.last_update_ns.store( SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, Ordering::Relaxed ); } pub fn get_best_bid(&self) -> Option<(u64, u64)> { self.bids.iter().next_back().map(|(p, q)| (*p, *q)) } pub fn get_best_ask(&self) -> Option<(u64, u64)> { self.asks.iter().next().map(|(p, q)| (*p, *q)) } pub fn get_mid_price(&self) -> Option { match (self.get_best_bid(), self.get_best_ask()) { (Some((bid, _)), Some((ask, _))) => Some((bid + ask) / 2), _ => None, } } } /// High-performance message queue for order flow #[derive(Debug)] pub struct HFTMessageQueue { queue: SegQueue, message_count: AtomicU64, } impl HFTMessageQueue { pub fn new() -> Self { Self { queue: SegQueue::new(), message_count: AtomicU64::new(0), } } pub fn push(&self, order: HFTOrder) { self.queue.push(order); self.message_count.fetch_add(1, Ordering::Relaxed); } pub fn pop(&self) -> Option { self.queue.pop() } pub fn len(&self) -> u64 { self.message_count.load(Ordering::Relaxed) } } // ===== BENCHMARK IMPLEMENTATIONS ===== /// Benchmark order validation performance fn bench_order_validation(c: &mut Criterion) { let risk_calculator = RiskCalculator::new(); let position_manager = PositionManager::new(); // Pre-populate some positions position_manager.update_position(1, 500); position_manager.update_position(2, -300); let orders: Vec = (0..1000).map(|i| HFTOrder { id: i, symbol: *b"AAPL ", side: if i % 2 == 0 { OrderSide::Buy } else { OrderSide::Sell }, quantity: 100 + (i % 900) as u64, price: 15000 + (i % 1000) as u64, // $150.00 + variation timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: (i % 10) as u16, }).collect(); let mut group = c.benchmark_group("order_validation"); group.throughput(Throughput::Elements(1)); group.bench_function("validate_single_order", |b| { b.iter_batched( || orders[black_box(0)].clone(), |order| { let current_position = position_manager.get_position(1); black_box(risk_calculator.validate_order(&order, current_position)) }, BatchSize::SmallInput ) }); group.bench_function("validate_order_batch", |b| { b.iter_batched( || orders[0..100].to_vec(), |order_batch| { for order in order_batch { let current_position = position_manager.get_position(1); black_box(risk_calculator.validate_order(&order, current_position)); } }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark market data processing performance fn bench_market_data_processing(c: &mut Criterion) { let ticks: Vec = (0..10000).map(|i| MarketTick { symbol_id: (i % 100) as u32, bid: 15000 + (i % 100) as u64, ask: 15001 + (i % 100) as u64, bid_size: 1000 + (i % 9000) as u32, ask_size: 1000 + (i % 9000) as u32, last: 15000 + (i % 100) as u64, volume: (i % 10000) as u32, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, }).collect(); let mut group = c.benchmark_group("market_data_processing"); group.throughput(Throughput::Elements(1)); group.bench_function("process_single_tick", |b| { b.iter_batched( || ticks[0], |tick| { // Simulate market data processing let mid_price = (tick.bid + tick.ask) / 2; let spread = tick.ask - tick.bid; black_box((mid_price, spread)) }, BatchSize::SmallInput ) }); group.bench_function("process_tick_batch", |b| { b.iter_batched( || &ticks[0..1000], |tick_batch| { for tick in tick_batch { let mid_price = (tick.bid + tick.ask) / 2; let spread = tick.ask - tick.bid; black_box((mid_price, spread)); } }, BatchSize::SmallInput ) }); group.bench_function("calculate_vwap", |b| { b.iter_batched( || &ticks[0..100], |tick_batch| { let mut total_notional = 0u64; let mut total_volume = 0u64; for tick in tick_batch { total_notional += tick.last * tick.volume as u64; total_volume += tick.volume as u64; } let vwap = if total_volume > 0 { total_notional / total_volume } else { 0 }; black_box(vwap) }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark position management performance fn bench_position_management(c: &mut Criterion) { let position_manager = PositionManager::new(); let mut group = c.benchmark_group("position_management"); group.throughput(Throughput::Elements(1)); group.bench_function("update_position", |b| { b.iter_batched( || (black_box(1u32), black_box(100i64)), |(symbol_id, quantity_delta)| { position_manager.update_position(symbol_id, quantity_delta) }, BatchSize::SmallInput ) }); group.bench_function("get_position", |b| { b.iter_batched( || black_box(1u32), |symbol_id| { black_box(position_manager.get_position(symbol_id)) }, BatchSize::SmallInput ) }); group.bench_function("calculate_pnl", |b| { // Pre-populate position position_manager.update_position(1, 1000); b.iter_batched( || (black_box(1u32), black_box(15050u64), black_box(15000u64)), |(symbol_id, current_price, entry_price)| { black_box(position_manager.calculate_pnl(symbol_id, current_price, entry_price)) }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark risk calculation performance fn bench_risk_calculations(c: &mut Criterion) { let risk_calculator = RiskCalculator::new(); // Generate test portfolio let positions: Vec<(u32, i64, u64)> = (0..100).map(|i| { (i as u32, 100 + (i * 10), 15000 + (i * 10) as u64) }).collect(); let mut group = c.benchmark_group("risk_calculations"); group.throughput(Throughput::Elements(1)); group.bench_function("calculate_var", |b| { b.iter_batched( || (positions.as_slice(), 0.95f32), |(positions, confidence)| { black_box(risk_calculator.calculate_var(positions, confidence)) }, BatchSize::SmallInput ) }); group.bench_function("portfolio_exposure", |b| { b.iter_batched( || positions.as_slice(), |positions| { let mut total_long = 0u64; let mut total_short = 0u64; for (_, quantity, price) in positions { let notional = quantity.abs() as u64 * price; if *quantity > 0 { total_long += notional; } else { total_short += notional; } } black_box((total_long, total_short)) }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark order book operations fn bench_order_book_operations(c: &mut Criterion) { let mut order_book = OrderBook::new(); // Pre-populate order book for i in 0..1000 { order_book.update_bid(15000 - i, 100); } let mut group = c.benchmark_group("order_book"); group.throughput(Throughput::Elements(1)); group.bench_function("update_bid", |b| { let mut ob = order_book; b.iter_batched( || (black_box(14500u64), black_box(200u64)), |(price, quantity)| { ob.update_bid(price, quantity) }, BatchSize::SmallInput ) }); group.bench_function("get_best_bid", |b| { b.iter(|| { black_box(order_book.get_best_bid()) }) }); group.bench_function("get_mid_price", |b| { b.iter(|| { black_box(order_book.get_mid_price()) }) }); group.finish(); } /// Benchmark message queue performance fn bench_message_queue(c: &mut Criterion) { let queue = HFTMessageQueue::new(); let orders: Vec = (0..10000).map(|i| HFTOrder { id: i, symbol: *b"AAPL ", side: if i % 2 == 0 { OrderSide::Buy } else { OrderSide::Sell }, quantity: 100, price: 15000, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: 1, }).collect(); let mut group = c.benchmark_group("message_queue"); group.throughput(Throughput::Elements(1)); group.bench_function("push_order", |b| { b.iter_batched( || orders[0].clone(), |order| { queue.push(order) }, BatchSize::SmallInput ) }); // Pre-populate queue for pop benchmark for order in &orders[0..1000] { queue.push(order.clone()); } group.bench_function("pop_order", |b| { b.iter(|| { black_box(queue.pop()) }) }); group.bench_function("queue_throughput", |b| { b.iter_batched( || orders[0..100].to_vec(), |order_batch| { for order in order_batch { queue.push(order); } for _ in 0..100 { queue.pop(); } }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark serialization performance fn bench_serialization(c: &mut Criterion) { let order = HFTOrder { id: 12345, symbol: *b"AAPL ", side: OrderSide::Buy, quantity: 1000, price: 15050, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: 1, }; let mut group = c.benchmark_group("serialization"); group.throughput(Throughput::Bytes(std::mem::size_of::() as u64)); group.bench_function("bincode_serialize", |b| { b.iter_batched( || order.clone(), |order| { black_box(bincode::serialize(&order).unwrap()) }, BatchSize::SmallInput ) }); let serialized = bincode::serialize(&order).unwrap(); group.bench_function("bincode_deserialize", |b| { b.iter_batched( || serialized.clone(), |data| { black_box(bincode::deserialize::(&data).unwrap()) }, BatchSize::SmallInput ) }); group.bench_function("json_serialize", |b| { b.iter_batched( || order.clone(), |order| { black_box(serde_json::to_string(&order).unwrap()) }, BatchSize::SmallInput ) }); let json_data = serde_json::to_string(&order).unwrap(); group.bench_function("json_deserialize", |b| { b.iter_batched( || json_data.clone(), |data| { black_box(serde_json::from_str::(&data).unwrap()) }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark memory allocation patterns fn bench_memory_patterns(c: &mut Criterion) { let mut group = c.benchmark_group("memory_patterns"); group.bench_function("vec_allocation", |b| { b.iter(|| { let mut vec = Vec::with_capacity(1000); for i in 0..1000 { vec.push(black_box(i)); } black_box(vec) }) }); group.bench_function("hashmap_insertion", |b| { b.iter(|| { let mut map = HashMap::with_capacity(1000); for i in 0..1000 { map.insert(black_box(i), black_box(i * 2)); } black_box(map) }) }); group.bench_function("btreemap_insertion", |b| { b.iter(|| { let mut map = BTreeMap::new(); for i in 0..1000 { map.insert(black_box(i), black_box(i * 2)); } black_box(map) }) }); group.finish(); } /// Benchmark financial calculations fn bench_financial_calculations(c: &mut Criterion) { let prices = vec![150.0, 151.5, 149.8, 152.1, 150.9]; let returns: Vec = prices.windows(2).map(|w| (w[1] - w[0]) / w[0]).collect(); let mut group = c.benchmark_group("financial_calculations"); group.bench_function("simple_return", |b| { b.iter_batched( || (black_box(150.0), black_box(151.5)), |(start_price, end_price)| { black_box((end_price - start_price) / start_price) }, BatchSize::SmallInput ) }); group.bench_function("volatility_calculation", |b| { b.iter_batched( || returns.clone(), |returns| { let mean = returns.iter().sum::() / returns.len() as f64; let variance = returns.iter() .map(|r| (r - mean).powi(2)) .sum::() / returns.len() as f64; black_box(variance.sqrt()) }, BatchSize::SmallInput ) }); group.bench_function("sharpe_ratio", |b| { b.iter_batched( || (returns.clone(), 0.02f64), // 2% risk-free rate |(returns, risk_free_rate)| { let mean_return = returns.iter().sum::() / returns.len() as f64; let std_dev = { let variance = returns.iter() .map(|r| (r - mean_return).powi(2)) .sum::() / returns.len() as f64; variance.sqrt() }; black_box((mean_return - risk_free_rate) / std_dev) }, BatchSize::SmallInput ) }); group.finish(); } /// Benchmark timestamp operations fn bench_timestamp_operations(c: &mut Criterion) { let mut group = c.benchmark_group("timestamp_operations"); group.bench_function("system_time_now", |b| { b.iter(|| { black_box(SystemTime::now()) }) }); group.bench_function("unix_timestamp_ns", |b| { b.iter(|| { black_box(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos()) }) }); group.bench_function("chrono_utc_now", |b| { b.iter(|| { black_box(Utc::now()) }) }); group.bench_function("timestamp_comparison", |b| { let ts1 = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64; let ts2 = ts1 + 1000; // 1μs later b.iter(|| { black_box(ts2 > ts1) }) }); group.finish(); } // ===== CRITERION CONFIGURATION ===== criterion_group! { name = hft_benchmarks; config = Criterion::default() .measurement_time(Duration::from_secs(10)) .sample_size(1000) .warm_up_time(Duration::from_secs(3)); targets = bench_order_validation, bench_market_data_processing, bench_position_management, bench_risk_calculations, bench_order_book_operations, bench_message_queue, bench_serialization, bench_memory_patterns, bench_financial_calculations, bench_timestamp_operations } criterion_main!(hft_benchmarks); // ===== PERFORMANCE VALIDATION TESTS ===== #[cfg(test)] mod performance_validation_tests { use super::*; use std::time::Instant; #[test] fn test_order_validation_performance() { let risk_calculator = RiskCalculator::new(); let order = HFTOrder { id: 1, symbol: *b"AAPL ", side: OrderSide::Buy, quantity: 100, price: 15000, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: 1, }; let iterations = 10000; let start = Instant::now(); for _ in 0..iterations { black_box(risk_calculator.validate_order(&order, 500)); } let duration = start.elapsed(); let avg_duration_ns = duration.as_nanos() / iterations; // Should validate orders in less than 1μs (1000ns) assert!(avg_duration_ns < 1000, "Order validation too slow: {}ns average", avg_duration_ns); } #[test] fn test_market_data_processing_performance() { let tick = MarketTick { symbol_id: 1, bid: 15000, ask: 15001, bid_size: 1000, ask_size: 1000, last: 15000, volume: 5000, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, }; let iterations = 100000; let start = Instant::now(); for _ in 0..iterations { let mid_price = (tick.bid + tick.ask) / 2; let spread = tick.ask - tick.bid; black_box((mid_price, spread)); } let duration = start.elapsed(); let avg_duration_ns = duration.as_nanos() / iterations; // Should process market data in less than 100ns assert!(avg_duration_ns < 100, "Market data processing too slow: {}ns average", avg_duration_ns); } #[test] fn test_position_update_performance() { let position_manager = PositionManager::new(); let iterations = 10000; let start = Instant::now(); for i in 0..iterations { position_manager.update_position(1, if i % 2 == 0 { 100 } else { -100 }); } let duration = start.elapsed(); let avg_duration_ns = duration.as_nanos() / iterations; // Should update positions in less than 2μs (2000ns) assert!(avg_duration_ns < 2000, "Position update too slow: {}ns average", avg_duration_ns); } #[test] fn test_pnl_calculation_performance() { let position_manager = PositionManager::new(); position_manager.update_position(1, 1000); let iterations = 100000; let start = Instant::now(); for _ in 0..iterations { black_box(position_manager.calculate_pnl(1, 15050, 15000)); } let duration = start.elapsed(); let avg_duration_ns = duration.as_nanos() / iterations; // Should calculate PnL in less than 50ns assert!(avg_duration_ns < 50, "PnL calculation too slow: {}ns average", avg_duration_ns); } #[test] fn test_message_queue_performance() { let queue = HFTMessageQueue::new(); let order = HFTOrder { id: 1, symbol: *b"AAPL ", side: OrderSide::Buy, quantity: 100, price: 15000, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: 1, }; let iterations = 10000; let start = Instant::now(); for _ in 0..iterations { queue.push(order.clone()); } for _ in 0..iterations { queue.pop(); } let duration = start.elapsed(); let avg_duration_ns = duration.as_nanos() / (iterations * 2); // push + pop // Should handle queue operations in less than 1μs (1000ns) assert!(avg_duration_ns < 1000, "Message queue operations too slow: {}ns average", avg_duration_ns); } #[test] fn test_serialization_performance() { let order = HFTOrder { id: 12345, symbol: *b"AAPL ", side: OrderSide::Buy, quantity: 1000, price: 15050, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: 1, }; let iterations = 10000; let start = Instant::now(); for _ in 0..iterations { let serialized = bincode::serialize(&order).unwrap(); let _deserialized: HFTOrder = bincode::deserialize(&serialized).unwrap(); } let duration = start.elapsed(); let avg_duration_ns = duration.as_nanos() / iterations; // Should serialize/deserialize in less than 500ns assert!(avg_duration_ns < 500, "Serialization too slow: {}ns average", avg_duration_ns); } #[test] fn test_overall_system_latency() { // Simulate complete order processing workflow let risk_calculator = RiskCalculator::new(); let position_manager = PositionManager::new(); let queue = HFTMessageQueue::new(); let order = HFTOrder { id: 1, symbol: *b"AAPL ", side: OrderSide::Buy, quantity: 100, price: 15000, timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos() as u64, strategy_id: 1, }; let iterations = 1000; let start = Instant::now(); for _ in 0..iterations { // Step 1: Queue order queue.push(order.clone()); // Step 2: Get order from queue let order = queue.pop().unwrap(); // Step 3: Validate order let current_position = position_manager.get_position(1); let is_valid = risk_calculator.validate_order(&order, current_position); if is_valid { // Step 4: Update position let position_delta = match order.side { OrderSide::Buy => order.quantity as i64, OrderSide::Sell => -(order.quantity as i64), }; position_manager.update_position(1, position_delta); // Step 5: Calculate PnL position_manager.calculate_pnl(1, 15050, 15000); } } let duration = start.elapsed(); let avg_duration_us = duration.as_micros() / iterations; // Complete workflow should be under 10μs assert!(avg_duration_us < 10, "Overall system latency too high: {}μs average", avg_duration_us); println!("Performance Summary:"); println!(" Complete workflow latency: {}μs average", avg_duration_us); println!(" Theoretical throughput: {} orders/sec", 1_000_000 / avg_duration_us); } }