Expand description
Lock-free ring buffer implementation for zero-latency metric recording
This module provides a single-producer, multiple-consumer (SPMC) ring buffer optimized for high-frequency metric recording with minimal overhead.
§Architecture Overview
The ring buffer uses atomic operations to avoid locks in the hot path, ensuring minimal impact on trading system performance. It supports a single producer with multiple concurrent consumers.
Key Design Features:
- Zero-allocation in hot paths (all memory pre-allocated)
- Lock-free operations using compare-and-swap
- Cache-friendly data layout with alignment
- Overflow tracking for capacity monitoring
§Capacity Requirements and Planning
§Power of 2 Requirement
MANDATORY: Capacity must be a power of 2 (2, 4, 8, 16, 32, 64, 128, …). This enables:
- Bitwise AND operations (
& capacity_mask) instead of expensive modulo - ~10-20% performance improvement in hot paths
- Optimal cache line alignment for atomic operations
§Effective Capacity
Important: Effective capacity is capacity - 1 due to full/empty distinction.
The buffer reserves one slot to differentiate between full and empty states.
§Capacity Planning Formula
Required_Capacity = Peak_Throughput_Per_Second × Max_Consumer_Delay_Seconds × Safety_Factor
Where:
- Peak_Throughput_Per_Second: Maximum messages/second during bursts
- Max_Consumer_Delay_Seconds: Worst-case time between drain operations
- Safety_Factor: 2-5x buffer (3x recommended for production)§HFT Capacity Guidelines by Use Case
| Use Case | Frequency | Recommended Capacity | Effective Items | Memory (8-byte) | Cache Level |
|---|---|---|---|---|---|
| Trade execution | Sub-μs | 64-128 | 63-127 | ~0.5-1KB | L1 |
| Order book updates | μs | 512-1024 | 511-1023 | ~4-8KB | L1/L2 |
| Risk calculations | ms | 2048-4096 | 2047-4095 | ~16-32KB | L2 |
| Strategy signals | s | 8192-16384 | 8191-16383 | ~64-128KB | L2/L3 |
| Historical logging | min+ | 32768+ | 32767+ | ~256KB+ | Memory |
§Performance Characteristics by Capacity
- 64-256 items: Ultra-low latency (~10-50ns), fits in L1 cache (32-64KB)
- 512-2048 items: High performance (~50-100ns), excellent L2 utilization
- 4096-8192 items: Balanced (~100-200ns), L3 cache friendly
- 16384+ items: High capacity (~200ns+), may cause cache misses
§Memory Usage Calculation
Total Memory = capacity × size_of::<T>() + overhead (~64 bytes)
Examples:
- capacity=64, T=u64: ~576 bytes (fits in single cache line group)
- capacity=1024, T=u64: ~8KB (good L1 cache utilization)
- capacity=8192, T=u64: ~64KB (fits comfortably in L2 cache)§Overflow Monitoring and Capacity Adjustment
§Overflow Implications
Each overflow represents:
- Lost metric data that could impact trading decisions
- Potential performance degradation due to buffer pressure
- Need for capacity adjustment or consumer optimization
§Production Monitoring Guidelines
- Zero overflows: Optimal capacity, consider reducing if over-provisioned
- Rare overflows (< 0.1%): Acceptable for non-critical metrics
- Regular overflows (> 1%): Increase capacity or optimize consumers
- Frequent overflows (> 10%): Critical issue, system overload
§Capacity Adjustment Strategy
Based on overflow rate per minute:
- > 100 overflows/min: Double capacity immediately
- 10-100 overflows/min: Increase capacity by 50%
- 1-10 overflows/min: Monitor trends, minor adjustment
- 0 overflows/min: Current capacity sufficient
§Usage Examples
§Basic Usage
use crate::monitoring::ring_buffer::SharedRingBuffer;
// Create buffer for order book updates
let buffer = SharedRingBuffer::new(1024);
// Producer: Push metrics
if !buffer.push(metric_data) {
eprintln!("Buffer overflow! Consider increasing capacity");
}
// Consumer: Drain in batches
let metrics = buffer.drain(64);
for metric in metrics {
process_metric(metric);
}§Capacity Planning Examples
use crate::monitoring::ring_buffer::SharedRingBuffer;
// Example 1: High-frequency order book (50K updates/sec, 1ms drain)
// Required: 50,000 × 0.001 = 50, Safety: 4x = 200, Next power of 2: 256
let order_book = SharedRingBuffer::new(256);
// Example 2: Strategy signals (1K signals/sec, 10ms drain)
// Required: 1,000 × 0.01 = 10, Safety: 8x = 80, Next power of 2: 128
let strategy = SharedRingBuffer::new(128);
// Example 3: Risk metrics (10K metrics/sec, 5ms drain)
// Required: 10,000 × 0.005 = 50, Safety: 5x = 250, Next power of 2: 512
let risk = SharedRingBuffer::new(512);§Performance Optimization Guidelines
§Multi-Consumer Efficiency
- Use
drain(batch_size)instead of repeatedpop()calls - Recommended batch sizes: 16-64 items per drain operation
- Each consumer competes for items (no broadcasting)
§Overflow Handling Strategy
When buffer overflows:
- Data is lost (ring buffer does not resize)
- Monitor
overflow_count()for capacity planning - Consider: larger capacity, faster consumers, or multiple buffers
Structs§
- Ring
Buffer - A lock-free ring buffer for metric storage
- Shared
Ring Buffer - A thread-safe wrapper around RingBuffer for shared access