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>
405 lines
12 KiB
Rust
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");
|
|
}
|
|
}
|