#![allow(unused_crate_dependencies)] //! Comprehensive tests for lockfree queue implementations //! //! This test suite covers: //! - `LockFreeRingBuffer` (SPSC queue) with concurrency tests //! - `SmallBatchRing` with single/multi-threaded modes //! - `SharedMemoryChannel` for inter-service communication //! - Atomic operations (`AtomicMetrics`, `AtomicFlag`, `SequenceGenerator`) //! - Performance benchmarks for HFT requirements (<1μs latency) use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; use trading_engine::lockfree::{ atomic_ops::{AtomicFlag, AtomicMetrics, SequenceGenerator}, ring_buffer::{LockFreeRingBuffer, SPSCQueue}, small_batch_ring::{BatchMode, SmallBatchOrdersSoA, SmallBatchRing}, HftMessage, SharedMemoryChannel, }; // ============================================================================ // LockFreeRingBuffer (SPSC Queue) Tests // ============================================================================ #[test] fn test_spsc_basic_operations() { let queue = SPSCQueue::::new(8).expect("Failed to create SPSC queue"); // Test empty assert!(queue.is_empty()); assert!(!queue.is_full()); assert_eq!(queue.len(), 0); assert_eq!(queue.try_pop(), None); // Test push/pop queue.try_push(42).unwrap(); assert!(!queue.is_empty()); assert_eq!(queue.len(), 1); assert_eq!(queue.try_pop(), Some(42)); assert!(queue.is_empty()); } #[test] fn test_spsc_capacity_validation() { // Zero capacity should fail LockFreeRingBuffer::::new(0).unwrap_err(); // Non-power-of-2 should fail LockFreeRingBuffer::::new(3).unwrap_err(); LockFreeRingBuffer::::new(7).unwrap_err(); LockFreeRingBuffer::::new(100).unwrap_err(); // Power-of-2 should succeed LockFreeRingBuffer::::new(2).unwrap(); LockFreeRingBuffer::::new(4).unwrap(); LockFreeRingBuffer::::new(8).unwrap(); LockFreeRingBuffer::::new(1024).unwrap(); } #[test] fn test_spsc_full_condition() { let queue = LockFreeRingBuffer::::new(4).expect("Failed to create queue"); // Fill to usable capacity (3 items for capacity-4 SPSC) // SPSC ring buffers reserve one slot to distinguish full from empty for i in 0..3 { assert!(queue.try_push(i).is_ok(), "Failed to push item {}", i); } assert!(queue.is_full()); assert_eq!(queue.len(), 3); // Should fail when full assert!(queue.try_push(99).is_err()); // Pop one item assert_eq!(queue.try_pop(), Some(0)); assert!(!queue.is_full()); // Should succeed now queue.try_push(99).unwrap(); } #[test] fn test_spsc_wraparound() { let queue = LockFreeRingBuffer::::new(4).expect("Failed to create queue"); // Test multiple wraparounds for cycle in 0..10 { for i in 0..4 { let value = cycle * 4 + i; queue.try_push(value).unwrap(); assert_eq!(queue.try_pop(), Some(value)); } } assert!(queue.is_empty()); } #[test] fn test_spsc_utilization() { let queue = LockFreeRingBuffer::::new(8).expect("Failed to create queue"); assert_eq!(queue.utilization(), 0.0); queue.try_push(1).unwrap(); assert!((queue.utilization() - 0.125).abs() < 0.01); // 1/8 queue.try_push(2).unwrap(); assert!((queue.utilization() - 0.25).abs() < 0.01); // 2/8 // Fill to usable capacity (7 items for capacity-8 SPSC) for i in 3..=7 { queue.try_push(i).unwrap(); } // Utilization at full capacity: 7/8 = 0.875 assert!((queue.utilization() - 0.875).abs() < 0.01); } #[test] fn test_spsc_concurrent_single_producer_consumer() { let queue = Arc::new(LockFreeRingBuffer::::new(1024).expect("Failed to create queue")); let queue_consumer = Arc::clone(&queue); const NUM_ITEMS: u64 = 10_000; // Reduced for faster testing // Producer thread let producer = thread::spawn(move || { for i in 0..NUM_ITEMS { let mut retries = 0; while queue.try_push(i).is_err() { thread::yield_now(); retries += 1; if retries > 10000 { panic!("Producer stuck at {}", i); } } } }); // Consumer thread let consumer = thread::spawn(move || { let mut received = Vec::new(); let start = Instant::now(); while received.len() < NUM_ITEMS as usize { if let Some(item) = queue_consumer.try_pop() { received.push(item); } else { thread::yield_now(); } // Timeout check if start.elapsed().as_secs() > 10 { panic!("Consumer timeout after {} items", received.len()); } } received }); producer.join().expect("Producer failed"); let received = consumer.join().expect("Consumer failed"); // Verify order and completeness assert_eq!(received.len(), NUM_ITEMS as usize); for (i, item) in received.into_iter().enumerate() { assert_eq!(item, i as u64, "Item out of order at index {}", i); } } #[test] fn test_spsc_performance_latency() { let queue = LockFreeRingBuffer::::new(8192).expect("Failed to create queue"); const NUM_OPERATIONS: usize = 100_000; let start = Instant::now(); // Measure push+pop latency for i in 0..NUM_OPERATIONS { queue.try_push(i as u64).expect("Push failed"); let value = queue.try_pop().expect("Pop failed"); assert_eq!(value, i as u64); } let duration = start.elapsed(); let avg_latency_ns = duration.as_nanos() / (NUM_OPERATIONS * 2) as u128; println!("SPSC performance:"); println!(" Average latency: {}ns per operation", avg_latency_ns); println!( " Throughput: {:.0} ops/sec", (NUM_OPERATIONS * 2) as f64 / duration.as_secs_f64() ); // HFT requirement: sub-microsecond latency #[cfg(not(debug_assertions))] assert!( avg_latency_ns < 1000, "Latency too high: {}ns > 1000ns", avg_latency_ns ); #[cfg(debug_assertions)] assert!( avg_latency_ns < 100_000, "Latency too high for debug: {}ns", avg_latency_ns ); } #[test] #[ignore] // Slow test - run with --ignored fn test_spsc_stress_test() { let queue = Arc::new(LockFreeRingBuffer::::new(256).expect("Failed to create queue")); let queue_consumer = Arc::clone(&queue); const STRESS_ITEMS: usize = 100_000; // Reduced for faster testing let producer = thread::spawn(move || { for i in 0..STRESS_ITEMS { let mut retries = 0; while queue.try_push(i as u64).is_err() { thread::yield_now(); retries += 1; if retries > 10000 { panic!("Producer stuck at item {}", i); } } } }); let consumer = thread::spawn(move || { let mut count = 0; let mut prev = None; let start = Instant::now(); while count < STRESS_ITEMS { if let Some(item) = queue_consumer.try_pop() { if let Some(p) = prev { assert_eq!(item, p + 1, "Out of order at {}", count); } prev = Some(item); count += 1; } else { thread::yield_now(); } // Timeout check to prevent infinite loops if start.elapsed().as_secs() > 30 { panic!("Consumer timeout after {} items", count); } } }); producer.join().expect("Producer failed"); consumer.join().expect("Consumer failed"); } // ============================================================================ // SmallBatchRing Tests // ============================================================================ #[test] fn test_small_batch_ring_creation() { let ring = SmallBatchRing::::new(8, BatchMode::SingleThreaded).expect("Failed to create ring"); assert_eq!(ring.capacity(), 8); assert_eq!(ring.len(), 0); assert!(ring.is_empty()); assert!(!ring.is_full()); assert_eq!(ring.batch_mode(), BatchMode::SingleThreaded); } #[test] fn test_small_batch_push_pop() { let ring = SmallBatchRing::::new(16, BatchMode::SingleThreaded).expect("Failed to create ring"); // Push batch let items = [1, 2, 3, 4, 5]; let pushed = ring.push_batch(&items).expect("Failed to push batch"); assert_eq!(pushed, 5); assert_eq!(ring.len(), 5); // Pop batch let mut output = [0_u32; 3]; let popped = ring.pop_batch(&mut output); assert_eq!(popped, 3); assert_eq!(output, [1, 2, 3]); assert_eq!(ring.len(), 2); // Pop remaining let mut remaining = [0_u32; 5]; let remaining_count = ring.pop_batch(&mut remaining); assert_eq!(remaining_count, 2); assert_eq!(remaining[0..2], [4, 5]); assert!(ring.is_empty()); } #[test] fn test_small_batch_mode_switching() { let mut ring = SmallBatchRing::::new(8, BatchMode::MultiThreaded).expect("Failed to create ring"); assert_eq!(ring.batch_mode(), BatchMode::MultiThreaded); ring.set_single_threaded(); assert_eq!(ring.batch_mode(), BatchMode::SingleThreaded); ring.set_multi_threaded(); assert_eq!(ring.batch_mode(), BatchMode::MultiThreaded); } #[test] fn test_small_batch_overflow_handling() { let ring = SmallBatchRing::::new(8, BatchMode::SingleThreaded).expect("Failed to create ring"); // Push 10 items to 8-capacity ring let items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let result = ring.push_batch(&items); // Should push only what fits result.unwrap(); let pushed = result.unwrap(); assert_eq!(pushed, 8); assert!(ring.is_full()); } #[test] fn test_small_batch_single_vs_multi_threaded() { let items = [1_u64, 2, 3, 4, 5, 6, 7, 8]; // Single-threaded mode let st_ring = SmallBatchRing::::new(16, BatchMode::SingleThreaded) .expect("Failed to create ST ring"); let st_pushed = st_ring.push_batch(&items).unwrap(); let mut st_output = [0_u64; 8]; let st_popped = st_ring.pop_batch(&mut st_output); // Multi-threaded mode let mt_ring = SmallBatchRing::::new(16, BatchMode::MultiThreaded).expect("Failed to create MT ring"); let mt_pushed = mt_ring.push_batch(&items).unwrap(); let mut mt_output = [0_u64; 8]; let mt_popped = mt_ring.pop_batch(&mut mt_output); // Both should produce same results assert_eq!(st_pushed, mt_pushed); assert_eq!(st_popped, mt_popped); assert_eq!(st_output, mt_output); assert_eq!(st_output, items); } #[test] fn test_small_batch_performance() { let ring = SmallBatchRing::::new(1024, BatchMode::SingleThreaded).expect("Failed to create ring"); const NUM_BATCHES: usize = 10_000; const BATCH_SIZE: usize = 8; let start = Instant::now(); for batch_id in 0..NUM_BATCHES { let mut items = [0_u64; BATCH_SIZE]; for i in 0..BATCH_SIZE { items[i] = (batch_id * BATCH_SIZE + i) as u64; } ring.push_batch(&items).expect("Failed to push batch"); let mut output = [0_u64; BATCH_SIZE]; let popped = ring.pop_batch(&mut output); assert_eq!(popped, BATCH_SIZE); assert_eq!(output, items); } let duration = start.elapsed(); let ops_per_sec = (NUM_BATCHES * BATCH_SIZE * 2) as f64 / duration.as_secs_f64(); let avg_latency_ns = duration.as_nanos() / (NUM_BATCHES * 2) as u128; println!("SmallBatchRing performance:"); println!(" Operations per second: {:.0}", ops_per_sec); println!(" Average latency per batch: {}ns", avg_latency_ns); // Should be faster than 500ns per batch operation assert!( avg_latency_ns < 500, "Latency too high: {}ns", avg_latency_ns ); } // ============================================================================ // SmallBatchOrdersSoA Tests // ============================================================================ #[test] fn test_soa_basic_operations() { let mut soa = SmallBatchOrdersSoA::new(); assert_eq!(soa.count, 0); // Add orders assert!(soa.add_order(1, 0x123, 0, 1, 1.5, 50000.0, 1000)); assert!(soa.add_order(2, 0x456, 1, 0, 2.0, 3000.0, 2000)); assert_eq!(soa.count, 2); // Test SIMD-friendly access let prices = soa.prices_simd(); assert_eq!(prices.len(), 2); assert_eq!(prices[0], 50000.0); assert_eq!(prices[1], 3000.0); let quantities = soa.quantities_simd(); assert_eq!(quantities.len(), 2); assert_eq!(quantities[0], 1.5); assert_eq!(quantities[1], 2.0); } #[test] fn test_soa_capacity_limit() { let mut soa = SmallBatchOrdersSoA::new(); // Add 8 orders (max capacity) for i in 0..8 { assert!(soa.add_order(i, 0, 0, 0, 1.0, 100.0, i)); } // 9th order should fail assert!(!soa.add_order(9, 0, 0, 0, 1.0, 100.0, 9)); assert_eq!(soa.count, 8); } #[test] fn test_soa_notional_calculation() { let mut soa = SmallBatchOrdersSoA::new(); soa.add_order(1, 0, 0, 1, 1.5, 50000.0, 1000); soa.add_order(2, 0, 1, 0, 2.0, 3000.0, 2000); soa.add_order(3, 0, 0, 1, 0.5, 100.0, 3000); let total = soa.calculate_total_notional_scalar(); let expected = 1.5 * 50000.0 + 2.0 * 3000.0 + 0.5 * 100.0; // 75000 + 6000 + 50 = 81050 assert!((total - expected).abs() < 1e-6); // Test SIMD version (if available) #[cfg(target_arch = "x86_64")] { let total_simd = soa.calculate_total_notional_simd(); assert!((total_simd - expected).abs() < 1e-6); } } #[test] fn test_soa_clear() { let mut soa = SmallBatchOrdersSoA::new(); soa.add_order(1, 0, 0, 1, 1.0, 100.0, 1000); soa.add_order(2, 0, 1, 0, 2.0, 200.0, 2000); assert_eq!(soa.count, 2); soa.clear(); assert_eq!(soa.count, 0); // Should be able to add again assert!(soa.add_order(3, 0, 0, 1, 3.0, 300.0, 3000)); assert_eq!(soa.count, 1); } // ============================================================================ // SharedMemoryChannel Tests // ============================================================================ #[test] fn test_shared_memory_channel_creation() { let channel = SharedMemoryChannel::new(1024).expect("Failed to create channel"); let stats = channel.get_stats(); assert_eq!(stats.messages_sent, 0); assert_eq!(stats.messages_received, 0); assert_eq!(stats.send_failures, 0); } #[test] fn test_shared_memory_channel_basic_send_receive() { let channel = SharedMemoryChannel::new(1024).expect("Failed to create channel"); let message = HftMessage::new(1, [1, 2, 3, 4, 5, 6, 7, 8]); channel.send(message).unwrap(); if let Some(received) = channel.try_receive() { assert_eq!(received.msg_type, 1); assert_eq!(received.payload, [1, 2, 3, 4, 5, 6, 7, 8]); } else { panic!("Message not received"); } let stats = channel.get_stats(); assert_eq!(stats.messages_sent, 1); assert_eq!(stats.messages_received, 1); } #[test] fn test_shared_memory_channel_full_condition() { let channel = SharedMemoryChannel::new(4).expect("Failed to create channel"); let message = HftMessage::new(1, [0; 8]); // Fill buffer to usable capacity (3 items for capacity-4 SPSC) // SharedMemoryChannel uses SPSC ring buffer which reserves one slot for _ in 0..3 { channel.send(message).unwrap(); } // Should fail when full assert!(channel.send(message).is_err()); let stats = channel.get_stats(); assert_eq!(stats.messages_sent, 3); assert_eq!(stats.send_failures, 1); } #[test] fn test_shared_memory_channel_latency_tracking() { let channel = SharedMemoryChannel::new(1024).expect("Failed to create channel"); let message = HftMessage::new(1, [0; 8]); // Send multiple messages for _ in 0..100 { channel.send(message).expect("Send failed"); } let stats = channel.get_stats(); assert_eq!(stats.messages_sent, 100); assert!(stats.avg_latency_ns > 0); assert!(stats.max_latency_ns > 0); assert!(stats.max_latency_ns >= stats.avg_latency_ns); } #[test] #[ignore] // Slow throughput test fn test_shared_memory_channel_throughput() { let channel = SharedMemoryChannel::new(8192).expect("Failed to create channel"); let message = HftMessage::new(1, [0; 8]); const NUM_MESSAGES: usize = 1_000; // Reduced for faster testing let start = Instant::now(); for _ in 0..NUM_MESSAGES { while channel.send(message).is_err() { thread::yield_now(); } } let duration = start.elapsed(); let msgs_per_sec = NUM_MESSAGES as f64 / duration.as_secs_f64(); println!( "SharedMemoryChannel throughput: {:.0} msgs/sec", msgs_per_sec ); // Should handle >10K messages/sec (relaxed for test speed) assert!( msgs_per_sec > 10_000.0, "Throughput too low: {:.0} msgs/sec", msgs_per_sec ); } // ============================================================================ // AtomicMetrics Tests // ============================================================================ #[test] fn test_atomic_metrics_basic() { let metrics = AtomicMetrics::new(); metrics.record_operation(100); metrics.record_operation(200); metrics.record_operation(50); metrics.record_error(); metrics.record_bytes(1024); let snapshot = metrics.snapshot(); assert_eq!(snapshot.operations_count, 3); assert_eq!(snapshot.avg_latency_ns, (100 + 200 + 50) / 3); assert_eq!(snapshot.min_latency_ns, 50); assert_eq!(snapshot.max_latency_ns, 200); assert_eq!(snapshot.errors_count, 1); assert_eq!(snapshot.bytes_processed, 1024); } #[test] fn test_atomic_metrics_reset() { let metrics = AtomicMetrics::new(); metrics.record_operation(100); metrics.record_error(); let snapshot1 = metrics.snapshot(); assert_eq!(snapshot1.operations_count, 1); metrics.reset(); let snapshot2 = metrics.snapshot(); assert_eq!(snapshot2.operations_count, 0); assert_eq!(snapshot2.errors_count, 0); assert_eq!(snapshot2.min_latency_ns, u64::MAX); assert_eq!(snapshot2.max_latency_ns, 0); } #[test] fn test_atomic_metrics_concurrent() { let metrics = Arc::new(AtomicMetrics::new()); let num_threads = 8; let ops_per_thread = 1000; let mut handles = Vec::new(); for _ in 0..num_threads { let metrics_clone = Arc::clone(&metrics); let handle = thread::spawn(move || { for i in 0..ops_per_thread { let latency = 100 + (i % 100) as u64; metrics_clone.record_operation(latency); if i % 100 == 0 { metrics_clone.record_error(); } metrics_clone.record_bytes(64); } }); handles.push(handle); } for handle in handles { handle.join().expect("Thread failed"); } let snapshot = metrics.snapshot(); assert_eq!( snapshot.operations_count, (num_threads * ops_per_thread) as u64 ); assert_eq!( snapshot.errors_count, (num_threads * (ops_per_thread / 100)) as u64 ); assert_eq!( snapshot.bytes_processed, (num_threads * ops_per_thread * 64) as u64 ); } #[test] #[ignore] // Uses sleep - slow test fn test_atomic_metrics_operations_per_second() { let metrics = AtomicMetrics::new(); // Record operations over time for _ in 0..10 { metrics.record_operation(100); thread::sleep(Duration::from_millis(10)); } let ops_per_sec = metrics.operations_per_second(); println!("Operations per second: {:.0}", ops_per_sec); // Should be reasonable (not zero, not infinite) assert!(ops_per_sec > 0.0); assert!(ops_per_sec < 1_000_000.0); } // ============================================================================ // AtomicFlag Tests // ============================================================================ #[test] fn test_atomic_flag_basic() { let flag = AtomicFlag::new(); assert!(!flag.is_set()); flag.set(); assert!(flag.is_set()); flag.clear(); assert!(!flag.is_set()); } #[test] fn test_atomic_flag_test_and_set() { let flag = AtomicFlag::new(); // First test_and_set should return false (was not set) assert!(!flag.test_and_set()); assert!(flag.is_set()); // Second test_and_set should return true (was set) assert!(flag.test_and_set()); assert!(flag.is_set()); } #[test] fn test_atomic_flag_compare_and_swap() { let flag = AtomicFlag::new_with(false); // Successful swap let prev = flag.compare_and_swap(false, true); assert!(!prev); assert!(flag.is_set()); // Failed swap let prev2 = flag.compare_and_swap(false, true); assert!(prev2); // Returns current value on failure assert!(flag.is_set()); } #[test] fn test_atomic_flag_concurrent_race() { let flag = Arc::new(AtomicFlag::new()); let num_threads = 10; let mut handles = Vec::new(); for thread_id in 0..num_threads { let flag_clone = Arc::clone(&flag); let handle = thread::spawn(move || { // Each thread tries to be first let was_first = !flag_clone.test_and_set(); (thread_id, was_first) }); handles.push(handle); } let results: Vec<_> = handles .into_iter() .map(|h| h.join().expect("Thread failed")) .collect(); // Exactly one thread should win let winners = results.iter().filter(|(_, was_first)| *was_first).count(); assert_eq!(winners, 1); assert!(flag.is_set()); } // ============================================================================ // SequenceGenerator Tests // ============================================================================ #[test] fn test_sequence_generator_basic() { let gen = SequenceGenerator::new(); assert_eq!(gen.current(), 1); assert_eq!(gen.next(), 1); assert_eq!(gen.next(), 2); assert_eq!(gen.next(), 3); assert_eq!(gen.current(), 4); } #[test] fn test_sequence_generator_custom_start() { let gen = SequenceGenerator::new_with_start(100); assert_eq!(gen.current(), 100); assert_eq!(gen.next(), 100); assert_eq!(gen.next(), 101); } #[test] fn test_sequence_generator_reset() { let gen = SequenceGenerator::new(); gen.next(); gen.next(); assert_eq!(gen.current(), 3); gen.reset(0); assert_eq!(gen.current(), 0); assert_eq!(gen.next(), 0); } #[test] fn test_sequence_generator_concurrent_uniqueness() { let gen = Arc::new(SequenceGenerator::new()); let num_threads = 8; let increments_per_thread = 1000; let mut handles = Vec::new(); for _ in 0..num_threads { let gen_clone = Arc::clone(&gen); let handle = thread::spawn(move || { let mut sequences = Vec::new(); for _ in 0..increments_per_thread { sequences.push(gen_clone.next()); } sequences }); handles.push(handle); } let mut all_sequences = Vec::new(); for handle in handles { let sequences = handle.join().expect("Thread failed"); all_sequences.extend(sequences); } // Verify all sequences are unique all_sequences.sort_unstable(); assert_eq!(all_sequences.len(), num_threads * increments_per_thread); for window in all_sequences.windows(2) { assert_ne!(window[0], window[1], "Duplicate sequence found"); } } // ============================================================================ // Performance Benchmarks // ============================================================================ #[test] #[ignore] // Slow benchmark - run with --ignored fn benchmark_spsc_vs_crossbeam() { use std::sync::mpsc; const BENCH_ITEMS: usize = 100_000; // Our SPSC queue let our_queue = Arc::new(LockFreeRingBuffer::::new(1024).expect("Failed to create")); let our_consumer = Arc::clone(&our_queue); let start = Instant::now(); let producer = thread::spawn(move || { for i in 0..BENCH_ITEMS { while our_queue.try_push(i as u64).is_err() { thread::yield_now(); } } }); let consumer = thread::spawn(move || { let mut count = 0; while count < BENCH_ITEMS { if our_consumer.try_pop().is_some() { count += 1; } else { thread::yield_now(); } } }); producer.join().unwrap(); consumer.join().unwrap(); let our_time = start.elapsed(); // Stdlib mpsc channel let (tx, rx) = mpsc::channel(); let start = Instant::now(); let producer = thread::spawn(move || { for i in 0..BENCH_ITEMS { tx.send(i as u64).unwrap(); } }); let consumer = thread::spawn(move || { for _ in 0..BENCH_ITEMS { rx.recv().unwrap(); } }); producer.join().unwrap(); consumer.join().unwrap(); let mpsc_time = start.elapsed(); println!("Performance comparison (100K items):"); println!(" Our SPSC: {:?}", our_time); println!(" Stdlib MPSC: {:?}", mpsc_time); println!( " Speedup: {:.2}x", mpsc_time.as_secs_f64() / our_time.as_secs_f64() ); // Our implementation should be competitive or faster #[cfg(not(debug_assertions))] assert!( our_time < mpsc_time * 2, "Our SPSC is too slow compared to stdlib" ); } #[test] #[ignore] // Slow benchmark - run with --ignored fn benchmark_hft_latency_requirements() { let queue = LockFreeRingBuffer::::new(1024).expect("Failed to create"); // Warm up for i in 0..1000 { queue.try_push(i).unwrap(); queue.try_pop().unwrap(); } // Measure minimum latency let mut min_latency_ns = u128::MAX; const SAMPLES: usize = 10_000; for i in 0..SAMPLES { let start = Instant::now(); queue.try_push(i as u64).unwrap(); queue.try_pop().unwrap(); let latency = start.elapsed().as_nanos(); if latency < min_latency_ns { min_latency_ns = latency; } } println!("HFT latency benchmark:"); println!(" Minimum latency: {}ns (push + pop)", min_latency_ns); // HFT target: <1μs for release builds #[cfg(not(debug_assertions))] assert!( min_latency_ns < 1000, "Minimum latency too high: {}ns", min_latency_ns ); } #[test] #[ignore] // Slow benchmark - run with --ignored fn benchmark_throughput_1m_ops() { let queue = Arc::new(LockFreeRingBuffer::::new(8192).expect("Failed to create")); let queue_consumer = Arc::clone(&queue); const TARGET_OPS: usize = 1_000_000; let start = Instant::now(); let producer = thread::spawn(move || { for i in 0..TARGET_OPS { while queue.try_push(i as u64).is_err() { thread::yield_now(); } } }); let consumer = thread::spawn(move || { let mut count = 0; while count < TARGET_OPS { if queue_consumer.try_pop().is_some() { count += 1; } else { thread::yield_now(); } } }); producer.join().unwrap(); consumer.join().unwrap(); let duration = start.elapsed(); let ops_per_sec = TARGET_OPS as f64 / duration.as_secs_f64(); println!("Throughput benchmark (1M operations):"); println!(" Time: {:?}", duration); println!(" Throughput: {:.0} ops/sec", ops_per_sec); // Should handle >1M ops/sec #[cfg(not(debug_assertions))] assert!( ops_per_sec > 1_000_000.0, "Throughput too low: {:.0} ops/sec", ops_per_sec ); }