Files
foxhunt/adaptive-strategy/benches/tlob_performance.rs
jgrusewski 6093eac7bf 🔧 Tonic 0.14 Upgrade: Auto-generated and build system changes
Wave 64-65 cleanup: Proto regeneration and build system updates from Tonic 0.12→0.14 upgrade

Files updated:
- Cargo.lock: Dependency resolution for Tonic 0.14.2
- All build.rs: Updated for tonic-prost-build
- Proto files: Regenerated with tonic-prost 0.14
- Examples/tests: Updated for new gRPC API

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 07:34:26 +02:00

405 lines
12 KiB
Rust

#![allow(unused_crate_dependencies)]
//! TLOB Performance Benchmarks
//! Validates sub-50μs inference requirement with comprehensive testing
use adaptive_strategy::models::{ModelConfig, ModelFactory};
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use std::time::Duration;
/// Create realistic order book features that match actual market data patterns
fn create_realistic_order_book_features() -> Vec<f64> {
let mut features = Vec::with_capacity(51);
// Bid prices (decreasing from 100.00)
for i in 0..10 {
features.push(100.0 - (i as f64 * 0.01));
}
// Ask prices (increasing from 100.01)
for i in 0..10 {
features.push(100.01 + (i as f64 * 0.01));
}
// Bid volumes (realistic volume distribution)
let bid_volumes = [
1000.0, 1500.0, 800.0, 1200.0, 900.0, 1100.0, 750.0, 1300.0, 950.0, 1050.0,
];
features.extend_from_slice(&bid_volumes);
// Ask volumes (realistic volume distribution)
let ask_volumes = [
1100.0, 1400.0, 850.0, 1250.0, 950.0, 1150.0, 800.0, 1350.0, 1000.0, 1080.0,
];
features.extend_from_slice(&ask_volumes);
// Market data: last_price, volume, volatility, momentum
features.extend_from_slice(&[100.005, 5000.0, 0.025, 0.0015]);
// Microstructure features (7 values) - realistic market microstructure indicators
features.extend_from_slice(&[0.12, 0.18, 0.15, 0.22, 0.19, 0.08, 0.11]);
assert_eq!(
features.len(),
51,
"Feature vector must be exactly 51 elements"
);
features
}
/// Create volatile market conditions features
fn create_volatile_market_features() -> Vec<f64> {
let mut features = create_realistic_order_book_features();
// Increase volatility and momentum for stress testing
features[42] = 0.08; // Higher volatility
features[43] = 0.005; // Higher momentum
// Adjust microstructure features for volatile conditions
for i in 44..51 {
features[i] *= 2.0; // Amplify microstructure signals
}
features
}
/// Benchmark single TLOB prediction latency
fn bench_tlob_single_prediction(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let model = rt.block_on(async {
let mut config = ModelConfig::default();
config.batch_size = 1; // Single prediction
ModelFactory::create_model("tlob", "bench_single".to_string(), config)
.await
.unwrap()
});
let features = create_realistic_order_book_features();
// Configure benchmark for sub-50μs measurement
let mut group = c.benchmark_group("tlob_single_prediction");
group.measurement_time(Duration::from_secs(30)); // Longer measurement for accuracy
group.sample_size(1000); // Large sample size for statistical significance
group.bench_function("single_prediction", |b| {
b.to_async(&rt)
.iter(|| async { black_box(model.predict(&features).await.unwrap()) })
});
group.finish();
}
/// Benchmark TLOB prediction with different feature variations
fn bench_tlob_feature_variations(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let model = rt.block_on(async {
ModelFactory::create_model(
"tlob",
"bench_variations".to_string(),
ModelConfig::default(),
)
.await
.unwrap()
});
let test_cases = vec![
("normal_market", create_realistic_order_book_features()),
("volatile_market", create_volatile_market_features()),
];
let mut group = c.benchmark_group("tlob_feature_variations");
for (name, features) in test_cases {
group.bench_with_input(
BenchmarkId::new("prediction", name),
&features,
|b, features| {
b.to_async(&rt)
.iter(|| async { black_box(model.predict(features).await.unwrap()) })
},
);
}
group.finish();
}
/// Benchmark batch processing with different batch sizes
fn bench_tlob_batch_processing(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let batch_sizes = vec![1, 4, 8, 16, 32];
let mut group = c.benchmark_group("tlob_batch_processing");
for &batch_size in &batch_sizes {
let model = rt.block_on(async {
let mut config = ModelConfig::default();
config.batch_size = batch_size;
ModelFactory::create_model("tlob", format!("bench_batch_{}", batch_size), config)
.await
.unwrap()
});
// Create multiple feature sets for batch processing
let features_batch: Vec<Vec<f64>> = (0..batch_size)
.map(|_| create_realistic_order_book_features())
.collect();
group.bench_with_input(
BenchmarkId::new("batch", batch_size),
&features_batch,
|b, features_batch| {
b.to_async(&rt).iter(|| async {
// Simulate batch processing by making multiple predictions
let mut results = Vec::new();
for features in features_batch {
let result = model.predict(features).await.unwrap();
results.push(black_box(result));
}
results
})
},
);
}
group.finish();
}
/// Benchmark concurrent TLOB predictions (stress test)
fn bench_tlob_concurrent_predictions(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let model = rt.block_on(async {
ModelFactory::create_model(
"tlob",
"bench_concurrent".to_string(),
ModelConfig::default(),
)
.await
.unwrap()
});
// Wrap in Arc for sharing across concurrent tasks
let model = std::sync::Arc::new(model);
let features = create_realistic_order_book_features();
let concurrent_levels = vec![1, 2, 4, 8];
let mut group = c.benchmark_group("tlob_concurrent_predictions");
for &concurrency in &concurrent_levels {
group.bench_with_input(
BenchmarkId::new("concurrent", concurrency),
&concurrency,
|b, &concurrency| {
b.to_async(&rt).iter(|| async {
let mut tasks = Vec::new();
for _ in 0..concurrency {
let model_clone = model.clone();
let features_clone = features.clone();
let task = tokio::spawn(async move {
model_clone.predict(&features_clone).await.unwrap()
});
tasks.push(task);
}
// Wait for all predictions to complete
let results = futures::future::join_all(tasks).await;
black_box(results)
})
},
);
}
group.finish();
}
/// Benchmark memory allocation patterns
fn bench_tlob_memory_patterns(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let model = rt.block_on(async {
ModelFactory::create_model("tlob", "bench_memory".to_string(), ModelConfig::default())
.await
.unwrap()
});
let features = create_realistic_order_book_features();
let mut group = c.benchmark_group("tlob_memory_patterns");
// Test sustained prediction load
group.bench_function("sustained_predictions", |b| {
b.to_async(&rt).iter(|| async {
// Make 100 predictions in rapid succession to test memory patterns
for _ in 0..100 {
let _result = black_box(model.predict(&features).await.unwrap());
}
})
});
group.finish();
}
/// Benchmark TLOB model initialization and warmup
fn bench_tlob_initialization(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let mut group = c.benchmark_group("tlob_initialization");
group.bench_function("model_creation", |b| {
b.to_async(&rt).iter(|| async {
let config = ModelConfig::default();
black_box(
ModelFactory::create_model("tlob", "bench_init".to_string(), config)
.await
.unwrap(),
)
})
});
// Benchmark first prediction (warmup cost)
group.bench_function("first_prediction", |b| {
b.to_async(&rt).iter_batched(
|| {
// Setup: Create fresh model for each iteration
rt.block_on(async {
ModelFactory::create_model(
"tlob",
"bench_first".to_string(),
ModelConfig::default(),
)
.await
.unwrap()
})
},
|model| async move {
let features = create_realistic_order_book_features();
black_box(model.predict(&features).await.unwrap())
},
criterion::BatchSize::SmallInput,
)
});
group.finish();
}
/// Custom criterion configuration for HFT benchmarking
fn configure_criterion() -> Criterion {
Criterion::default()
.measurement_time(Duration::from_secs(30))
.sample_size(500)
.confidence_level(0.95)
.significance_level(0.05)
.warm_up_time(Duration::from_secs(5))
}
criterion_group! {
name = tlob_benches;
config = configure_criterion();
targets =
bench_tlob_single_prediction,
bench_tlob_feature_variations,
bench_tlob_batch_processing,
bench_tlob_concurrent_predictions,
bench_tlob_memory_patterns,
bench_tlob_initialization
}
criterion_main!(tlob_benches);
#[cfg(test)]
mod bench_tests {
#[test]
fn test_feature_generation() {
let features = create_realistic_order_book_features();
assert_eq!(features.len(), 51);
// Validate bid prices are decreasing
for i in 1..10 {
assert!(
features[i - 1] > features[i],
"Bid prices should be decreasing"
);
}
// Validate ask prices are increasing
for i in 11..20 {
assert!(
features[i - 1] < features[i],
"Ask prices should be increasing"
);
}
// Validate spread exists
let best_bid = features[0];
let best_ask = features[10];
assert!(best_ask > best_bid, "Ask should be higher than bid");
}
#[test]
fn test_volatile_market_features() {
let normal = create_realistic_order_book_features();
let volatile = create_volatile_market_features();
// Volatility should be higher
assert!(
volatile[42] > normal[42],
"Volatile market should have higher volatility"
);
// Momentum should be higher
assert!(
volatile[43] > normal[43],
"Volatile market should have higher momentum"
);
}
#[tokio::test]
async fn test_benchmark_model_creation() {
let model =
ModelFactory::create_model("tlob", "test_model".to_string(), ModelConfig::default())
.await;
assert!(
model.is_ok(),
"Should be able to create TLOB model for benchmarking"
);
let model = model.unwrap();
assert_eq!(model.model_type(), "tlob");
assert!(model.is_ready());
}
#[tokio::test]
async fn test_benchmark_prediction() {
let model = ModelFactory::create_model(
"tlob",
"test_prediction".to_string(),
ModelConfig::default(),
)
.await
.unwrap();
let features = create_realistic_order_book_features();
let result = model.predict(&features).await;
assert!(result.is_ok(), "Benchmark prediction should succeed");
let prediction = result.unwrap();
assert!(prediction.confidence >= 0.0 && prediction.confidence <= 1.0);
// Check metadata contains performance information
assert!(prediction.metadata.is_some());
let metadata = prediction.metadata.unwrap();
assert!(metadata.contains_key("model_type"));
assert_eq!(metadata["model_type"], "tlob");
}
}