Files
foxhunt/tests/benches/small_batch_performance.rs
jgrusewski eb5fe84e22 🔥 COMPILATION SUCCESS: Complete resolution of all 543+ compilation errors
ARCHITECTURAL ACHIEVEMENTS:
 Zero compilation errors across entire workspace
 Complete elimination of circular dependencies
 Proper configuration architecture with centralized config crate
 Fixed all type mismatches and missing fields
 Restored proper crate structure (config at root level)

MAJOR FIXES:
- Fixed 19 critical data crate compilation errors
- Resolved configuration struct field mismatches
- Fixed enum variant naming (CSV → Csv)
- Corrected type conversions (FromPrimitive, compression types)
- Fixed HashMap key types (u32 vs usize)
- Resolved TLOBProcessor constructor issues

WORKSPACE STATUS:
- All services compile successfully
- Trading Service:  Ready
- Backtesting Service:  Ready
- ML Training Service:  Ready
- TLI Client:  Ready

Only documentation warnings remain (3,316 warnings to be addressed)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-29 10:59:34 +02:00

427 lines
14 KiB
Rust

//! Small Batch Performance Benchmark
//!
//! Validates the optimizations for small batch order processing
//! Target: 10K+ orders/sec (sub-100μs latency) for 1-10 order batches
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use std::time::{Duration, Instant};
use common::OrderSide;
use common::OrderType;
use trading_engine::{
lockfree::{BatchMode, SmallBatchOrdersSoA, SmallBatchRing},
prelude::*,
};
/// Benchmark small batch processor vs standard processing
fn benchmark_small_batch_vs_standard(c: &mut Criterion) {
let mut group = c.benchmark_group("small_batch_vs_standard");
group.measurement_time(Duration::from_secs(10));
for batch_size in [1, 2, 4, 8, 10].iter() {
// Small batch optimized processor
group.bench_with_input(
BenchmarkId::new("optimized_processor", batch_size),
batch_size,
|b, &batch_size| {
let mut processor = SmallBatchProcessor::new();
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for i in 0..iters {
let start = Instant::now();
// Add orders to batch
for j in 0..batch_size {
let order = OrderRequest::new(
(i * batch_size + j) as u64,
"BTCUSD",
if j % 2 == 0 { Side::Buy } else { Side::Sell },
OrderType::Limit,
1000.0 + j as f64,
50000.0 + (j as f64 * 0.01),
);
processor.add_order(order).expect("Failed to add order");
}
// Process batch
let _result = processor.process_batch().expect("Failed to process batch");
let end = Instant::now();
total_duration += end.duration_since(start);
black_box(&_result);
}
total_duration
});
},
);
// Standard processing (simulated)
group.bench_with_input(
BenchmarkId::new("standard_processing", batch_size),
batch_size,
|b, &batch_size| {
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for i in 0..iters {
let start = Instant::now();
// Simulate standard order processing overhead
for j in 0..batch_size {
// Simulate heap allocation
let order_data = vec![(i * batch_size + j) as u64, 50000 + j, 1000 + j];
// Simulate validation
let _valid = order_data[1] > 0 && order_data[2] > 0;
// Simulate atomic operations (expensive)
let counter = std::sync::atomic::AtomicU64::new(0);
counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let _count = counter.load(std::sync::atomic::Ordering::SeqCst);
black_box(&order_data);
}
let end = Instant::now();
total_duration += end.duration_since(start);
}
total_duration
});
},
);
}
group.finish();
}
/// Benchmark lock-free ring buffer optimizations
fn benchmark_lockfree_optimizations(c: &mut Criterion) {
let mut group = c.benchmark_group("lockfree_optimizations");
group.measurement_time(Duration::from_secs(5));
// Standard lock-free ring buffer
group.bench_function("standard_lockfree", |b| {
let buffer = LockFreeRingBuffer::<u64>::new(1024).expect("Failed to create buffer");
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for i in 0..iters {
let start = Instant::now();
// Push small batch (1-8 items) one at a time
for j in 0..8 {
let _ = buffer.try_push(i * 8 + j);
}
// Pop small batch one at a time
for _ in 0..8 {
let _ = buffer.try_pop();
}
let end = Instant::now();
total_duration += end.duration_since(start);
}
total_duration
});
});
// Optimized small batch ring buffer
group.bench_function("optimized_small_batch", |b| {
let buffer = SmallBatchRing::<u64>::new(1024, BatchMode::SingleThreaded)
.expect("Failed to create buffer");
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for i in 0..iters {
let start = Instant::now();
// Push small batch as single operation
let items: Vec<u64> = (0..8).map(|j| i * 8 + j).collect();
let _ = buffer.push_batch(&items);
// Pop small batch as single operation
let mut output = [0u64; 8];
let _ = buffer.pop_batch(&mut output);
let end = Instant::now();
total_duration += end.duration_since(start);
black_box(&output);
}
total_duration
});
});
group.finish();
}
/// Benchmark SIMD optimizations for small batches
fn benchmark_simd_optimizations(c: &mut Criterion) {
let mut group = c.benchmark_group("simd_optimizations");
group.measurement_time(Duration::from_secs(5));
// Test structure-of-arrays layout
group.bench_function("structure_of_arrays", |b| {
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for _i in 0..iters {
let start = Instant::now();
let mut soa = SmallBatchOrdersSoA::new();
// Add orders in SoA layout
for j in 0..8 {
soa.add_order(
j as u64,
0x123456 + j as u64,
j % 2,
1,
1000.0 + j as f64,
50000.0 + j as f64,
1000 + j as u64,
);
}
// Calculate total notional using SIMD
#[cfg(target_arch = "x86_64")]
let _total_notional = soa.calculate_total_notional_simd();
#[cfg(not(target_arch = "x86_64"))]
let _total_notional = soa.calculate_total_notional_scalar();
let end = Instant::now();
total_duration += end.duration_since(start);
black_box(&soa);
}
total_duration
});
});
// Test array-of-structures layout (standard)
group.bench_function("array_of_structures", |b| {
// Use canonical Order types from common module
use common::OrderId;
use common::OrderSide;
use common::OrderType;
#[derive(Clone, Copy)]
struct BenchOrder {
order_id: u64,
symbol_hash: u64,
side: u8,
order_type: u8,
quantity: f64,
price: f64,
timestamp: u64,
}
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for _i in 0..iters {
let start = Instant::now();
let mut orders = Vec::with_capacity(8);
// Add orders in AoS layout
for j in 0..8 {
orders.push(BenchOrder {
order_id: j as u64,
symbol_hash: 0x123456 + j as u64,
side: j % 2,
order_type: 1,
quantity: 1000.0 + j as f64,
price: 50000.0 + j as f64,
timestamp: 1000 + j as u64,
});
}
// Calculate total notional (scalar)
let _total_notional: f64 = orders.iter().map(|o| o.price * o.quantity).sum();
let end = Instant::now();
total_duration += end.duration_since(start);
black_box(&orders);
}
total_duration
});
});
group.finish();
}
/// Benchmark memory allocation patterns
fn benchmark_memory_allocation(c: &mut Criterion) {
let mut group = c.benchmark_group("memory_allocation");
group.measurement_time(Duration::from_secs(5));
// Stack allocation
group.bench_function("stack_allocation", |b| {
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for i in 0..iters {
let start = Instant::now();
// Stack allocated array
let mut orders = [0u64; 10];
for j in 0..10 {
orders[j] = i * 10 + j as u64;
}
// Process stack array
let _sum: u64 = orders.iter().sum();
let end = Instant::now();
total_duration += end.duration_since(start);
black_box(&orders);
}
total_duration
});
});
// Heap allocation
group.bench_function("heap_allocation", |b| {
b.iter_custom(|iters| {
let mut total_duration = Duration::from_nanos(0);
for i in 0..iters {
let start = Instant::now();
// Heap allocated vector
let mut orders = Vec::with_capacity(10);
for j in 0..10 {
orders.push(i * 10 + j);
}
// Process heap vector
let _sum: u64 = orders.iter().sum();
let end = Instant::now();
total_duration += end.duration_since(start);
black_box(&orders);
}
total_duration
});
});
group.finish();
}
/// Comprehensive latency validation for small batches
fn benchmark_latency_validation(c: &mut Criterion) {
let mut group = c.benchmark_group("latency_validation");
group.measurement_time(Duration::from_secs(15));
group.bench_function("target_validation", |b| {
let mut processor = SmallBatchProcessor::new();
b.iter_custom(|iters| {
let mut latencies = Vec::with_capacity(iters as usize);
let mut under_100us = 0u64;
let mut under_50us = 0u64;
for i in 0..iters {
let start = Instant::now();
// Create small batch (5 orders)
for j in 0..5 {
let order = OrderRequest::new(
(i * 5 + j) as u64,
"EURUSD",
if j % 2 == 0 { Side::Buy } else { Side::Sell },
OrderType::Limit,
1000.0 + j as f64,
1.1000 + (j as f64 * 0.0001),
);
processor.add_order(order).expect("Failed to add order");
}
// Process batch
let _result = processor.process_batch().expect("Failed to process batch");
let latency = start.elapsed();
let latency_us = latency.as_micros() as u64;
latencies.push(latency_us);
if latency_us <= 100 {
under_100us += 1;
}
if latency_us <= 50 {
under_50us += 1;
}
black_box(&_result);
}
// Calculate statistics
if !latencies.is_empty() {
latencies.sort_unstable();
let avg_latency = latencies.iter().sum::<u64>() / latencies.len() as u64;
let p50_latency = latencies[latencies.len() / 2];
let p95_latency = latencies[(latencies.len() * 95) / 100];
let p99_latency = latencies[(latencies.len() * 99) / 100];
let percent_under_100us = (under_100us as f64 / iters as f64) * 100.0;
let percent_under_50us = (under_50us as f64 / iters as f64) * 100.0;
eprintln!("\nSmall Batch Latency Results:");
eprintln!(" Average latency: {}μs", avg_latency);
eprintln!(" P50 latency: {}μs", p50_latency);
eprintln!(" P95 latency: {}μs", p95_latency);
eprintln!(" P99 latency: {}μs", p99_latency);
eprintln!(" {:.1}% under 100μs target", percent_under_100us);
eprintln!(" {:.1}% under 50μs stretch target", percent_under_50us);
// Calculate throughput
let throughput = if avg_latency > 0 {
1_000_000.0 / avg_latency as f64 // Convert μs to ops/sec
} else {
0.0
};
eprintln!(" Throughput: {:.0} orders/sec", throughput);
if throughput >= 10000.0 {
eprintln!(" ✅ TARGET ACHIEVED: 10K+ orders/sec");
} else {
eprintln!(" ❌ TARGET MISSED: {:.0} < 10K orders/sec", throughput);
}
}
// Return total processing time for Criterion
Duration::from_micros(latencies.iter().sum::<u64>())
});
});
group.finish();
}
criterion_group!(
small_batch_benches,
benchmark_small_batch_vs_standard,
benchmark_lockfree_optimizations,
benchmark_simd_optimizations,
benchmark_memory_allocation,
benchmark_latency_validation
);
criterion_main!(small_batch_benches);