Files
foxhunt/tli/benches/serialization_benchmarks.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

533 lines
17 KiB
Rust

//! Serialization and data transformation performance benchmarks
//!
//! This benchmark suite measures the performance of protobuf serialization,
//! JSON conversion, and data transformation operations used in TLI.
//!
//! ============================================================================
//! TEMPORARILY DISABLED - Missing protobuf definitions
//! ============================================================================
//!
//! This benchmark file references protobuf types that don't exist in trading.proto:
//! - `Order` (standalone message) - proto only has OrderUpdateEvent and order fields in responses
//! - `ListOrdersResponse` - not defined in proto
//! - `OrderUpdate` - proto has OrderUpdateEvent instead
//! - `MetricValue` - proto has Metric instead
//!
//! TODO: Fix this benchmark by either:
//! 1. Adding the missing proto message definitions to tli/proto/trading.proto
//! 2. Rewriting benchmarks to use existing proto types (GetOrderStatusResponse, OrderUpdateEvent, Metric)
//! 3. Creating separate benchmark-specific proto messages
//!
//! Related files:
//! - tli/proto/trading.proto - contains actual protobuf definitions
//! - tli/build.rs - compiles proto files to Rust code
//!
//! See Wave 36 - Agent 1 for context
//! ============================================================================
#![allow(unused_crate_dependencies)]
// DISABLED: use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
// DISABLED: use prost::Message;
// DISABLED: use std::collections::HashMap;
// DISABLED: use std::time::Duration;
// DISABLED: use tli::proto::trading::*;
/*
/// Benchmark protobuf serialization
fn bench_protobuf_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("protobuf_serialization");
// Order serialization
let order = Order {
order_id: "ORDER_123456".to_string(),
symbol: "AAPL".to_string(),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: 150.0,
status: OrderStatus::New as i32,
filled_quantity: 0.0,
remaining_quantity: 100.0,
average_price: 0.0,
created_time_unix_nanos: 1640995200000000000,
updated_time_unix_nanos: 1640995200000000000,
};
group.bench_function("order_encode", |b| {
b.iter(|| {
let mut buf = Vec::new();
black_box(&order).encode(&mut buf).unwrap();
black_box(buf)
})
});
let encoded_order = {
let mut buf = Vec::new();
order.encode(&mut buf).unwrap();
buf
};
group.bench_function("order_decode", |b| {
b.iter(|| {
let decoded = Order::decode(black_box(encoded_order.as_slice())).unwrap();
black_box(decoded)
})
});
// Position serialization
let position = Position {
symbol: "AAPL".to_string(),
quantity: 100.0,
market_price: 150.0,
market_value: 15000.0,
average_cost: 140.0,
unrealized_pnl: 1000.0,
realized_pnl: 0.0,
};
group.bench_function("position_encode", |b| {
b.iter(|| {
let mut buf = Vec::new();
black_box(&position).encode(&mut buf).unwrap();
black_box(buf)
})
});
let encoded_position = {
let mut buf = Vec::new();
position.encode(&mut buf).unwrap();
buf
};
group.bench_function("position_decode", |b| {
b.iter(|| {
let decoded = Position::decode(black_box(encoded_position.as_slice())).unwrap();
black_box(decoded)
})
});
group.finish();
}
/// Benchmark JSON serialization
fn bench_json_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("json_serialization");
// Create test data structures
let submit_order_request = SubmitOrderRequest {
symbol: "AAPL".to_string(),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: Some(150.0),
};
group.bench_function("submit_order_request_to_json", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&submit_order_request)).unwrap();
black_box(json)
})
});
let json_string = serde_json::to_string(&submit_order_request).unwrap();
group.bench_function("submit_order_request_from_json", |b| {
b.iter(|| {
let request: SubmitOrderRequest =
serde_json::from_str(black_box(&json_string)).unwrap();
black_box(request)
})
});
// Metrics serialization
let metrics_response = GetMetricsResponse {
metrics: vec![
MetricValue {
name: "latency_p99".to_string(),
value: 0.025,
unit: "seconds".to_string(),
labels: HashMap::from([
("service".to_string(), "trading".to_string()),
("environment".to_string(), "production".to_string()),
]),
timestamp_unix_nanos: 1640995200000000000,
},
MetricValue {
name: "orders_per_second".to_string(),
value: 150.0,
unit: "ops/sec".to_string(),
labels: HashMap::from([("service".to_string(), "trading".to_string())]),
timestamp_unix_nanos: 1640995200000000000,
},
],
};
group.bench_function("metrics_response_to_json", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&metrics_response)).unwrap();
black_box(json)
})
});
let metrics_json = serde_json::to_string(&metrics_response).unwrap();
group.bench_function("metrics_response_from_json", |b| {
b.iter(|| {
let response: GetMetricsResponse =
serde_json::from_str(black_box(&metrics_json)).unwrap();
black_box(response)
})
});
group.finish();
}
/// Benchmark data transformation operations
fn bench_data_transformations(c: &mut Criterion) {
let mut group = c.benchmark_group("data_transformations");
// Order list transformation
let orders: Vec<Order> = (0..1000)
.map(|i| Order {
order_id: format!("ORDER_{:06}", i),
symbol: "AAPL".to_string(),
side: if i % 2 == 0 {
OrderSide::Buy
} else {
OrderSide::Sell
} as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: 150.0 + (i as f64 * 0.01),
status: if i % 3 == 0 {
OrderStatus::Filled
} else {
OrderStatus::New
} as i32,
filled_quantity: if i % 3 == 0 { 100.0 } else { 0.0 },
remaining_quantity: if i % 3 == 0 { 0.0 } else { 100.0 },
average_price: if i % 3 == 0 {
150.0 + (i as f64 * 0.01)
} else {
0.0
},
created_time_unix_nanos: 1640995200000000000 + (i as i64 * 1000000),
updated_time_unix_nanos: 1640995200000000000 + (i as i64 * 1000000),
})
.collect();
group.bench_function("filter_filled_orders", |b| {
b.iter(|| {
let filled_orders: Vec<&Order> = black_box(&orders)
.iter()
.filter(|o| o.status == OrderStatus::Filled as i32)
.collect();
black_box(filled_orders)
})
});
group.bench_function("calculate_total_quantity", |b| {
b.iter(|| {
let total: f64 = black_box(&orders).iter().map(|o| o.quantity).sum();
black_box(total)
})
});
group.bench_function("group_orders_by_symbol", |b| {
b.iter(|| {
let mut grouped: HashMap<String, Vec<&Order>> = HashMap::new();
for order in black_box(&orders) {
grouped
.entry(order.symbol.clone())
.or_insert_with(Vec::new)
.push(order);
}
black_box(grouped)
})
});
group.finish();
}
/// Benchmark large data structure serialization
fn bench_large_data_structures(c: &mut Criterion) {
let mut group = c.benchmark_group("large_data_structures");
group.measurement_time(Duration::from_secs(10));
let sizes = vec![100, 1000, 5000, 10000];
for size in sizes {
// Create large order list
let orders: Vec<Order> = (0..size)
.map(|i| Order {
order_id: format!("ORDER_{:06}", i),
symbol: format!("SYM{}", i % 100), // 100 different symbols
side: if i % 2 == 0 {
OrderSide::Buy
} else {
OrderSide::Sell
} as i32,
order_type: OrderType::Market as i32,
quantity: 100.0 + (i as f64),
price: 100.0 + (i as f64 * 0.01),
status: OrderStatus::New as i32,
filled_quantity: 0.0,
remaining_quantity: 100.0 + (i as f64),
average_price: 0.0,
created_time_unix_nanos: 1640995200000000000 + (i as i64 * 1000),
updated_time_unix_nanos: 1640995200000000000 + (i as i64 * 1000),
})
.collect();
let list_response = ListOrdersResponse {
orders: orders.clone(),
};
group.bench_with_input(
BenchmarkId::new("protobuf_encode_large", size),
&list_response,
|b, response: &ListOrdersResponse| {
b.iter(|| {
let mut buf = Vec::new();
black_box(response).encode(&mut buf).unwrap();
black_box(buf)
})
},
);
let encoded = {
let mut buf = Vec::new();
list_response.encode(&mut buf).unwrap();
buf
};
group.bench_with_input(
BenchmarkId::new("protobuf_decode_large", size),
&encoded,
|b, data| {
b.iter(|| {
let decoded = ListOrdersResponse::decode(black_box(data.as_slice())).unwrap();
black_box(decoded)
})
},
);
group.bench_with_input(
BenchmarkId::new("json_encode_large", size),
&list_response,
|b, response| {
b.iter(|| {
let json = serde_json::to_string(black_box(response)).unwrap();
black_box(json)
})
},
);
let json_data = serde_json::to_string(&list_response).unwrap();
group.bench_with_input(
BenchmarkId::new("json_decode_large", size),
&json_data,
|b, data| {
b.iter(|| {
let decoded: ListOrdersResponse =
serde_json::from_str(black_box(data)).unwrap();
black_box(decoded)
})
},
);
}
group.finish();
}
/// Benchmark streaming data serialization
fn bench_streaming_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("streaming_serialization");
// Order updates stream
let order_updates: Vec<OrderUpdate> = (0..100)
.map(|i| OrderUpdate {
order_id: format!("ORDER_{:06}", i),
symbol: "AAPL".to_string(),
status: if i % 3 == 0 {
OrderStatus::Filled
} else {
OrderStatus::PartiallyFilled
} as i32,
filled_quantity: (i as f64) * 10.0,
timestamp_unix_nanos: 1640995200000000000 + (i as i64 * 1000000),
})
.collect();
group.bench_function("order_updates_batch_encode", |b| {
b.iter(|| {
let mut encoded_updates = Vec::new();
for update in black_box(&order_updates) {
let mut buf = Vec::new();
update.encode(&mut buf).unwrap();
encoded_updates.push(buf);
}
black_box(encoded_updates)
})
});
// Metrics stream
let metric_updates: Vec<MetricValue> = (0..100)
.map(|i| MetricValue {
name: format!("metric_{}", i % 10),
value: (i as f64) * 1.5,
unit: "count".to_string(),
labels: HashMap::from([
("instance".to_string(), format!("server_{}", i % 5)),
("environment".to_string(), "production".to_string()),
]),
timestamp_unix_nanos: 1640995200000000000 + (i as i64 * 100000),
})
.collect();
group.bench_function("metrics_stream_encode", |b| {
b.iter(|| {
let mut encoded_metrics = Vec::new();
for metric in black_box(&metric_updates) {
let mut buf = Vec::new();
metric.encode(&mut buf).unwrap();
encoded_metrics.push(buf);
}
black_box(encoded_metrics)
})
});
group.finish();
}
/// Benchmark memory efficiency
fn bench_memory_efficiency(c: &mut Criterion) {
let mut group = c.benchmark_group("memory_efficiency");
// Compare different serialization formats
let order = Order {
order_id: "ORDER_123456".to_string(),
symbol: "AAPL".to_string(),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: 150.0,
status: OrderStatus::New as i32,
filled_quantity: 0.0,
remaining_quantity: 100.0,
average_price: 0.0,
created_time_unix_nanos: 1640995200000000000,
updated_time_unix_nanos: 1640995200000000000,
};
group.bench_function("protobuf_size_efficiency", |b| {
b.iter(|| {
let mut buf = Vec::new();
black_box(&order).encode(&mut buf).unwrap();
let size = buf.len();
black_box((buf, size))
})
});
group.bench_function("json_size_efficiency", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&order)).unwrap();
let size = json.len();
black_box((json, size))
})
});
group.bench_function("json_compact_size_efficiency", |b| {
b.iter(|| {
let json = serde_json::to_vec(black_box(&order)).unwrap();
let size = json.len();
black_box((json, size))
})
});
group.finish();
}
/// Benchmark concurrent serialization
fn bench_concurrent_serialization(c: &mut Criterion) {
use tokio::runtime::Runtime;
let rt = Runtime::new().unwrap();
let mut group = c.benchmark_group("concurrent_serialization");
group.measurement_time(Duration::from_secs(10));
let thread_counts = vec![1, 2, 4, 8];
for thread_count in thread_counts {
group.bench_with_input(
BenchmarkId::new("parallel_order_encoding", thread_count),
&thread_count,
|b, &tc| {
b.to_async(&rt).iter(|| async move {
let orders: Vec<Order> = (0..100)
.map(|i| Order {
order_id: format!("ORDER_{:06}", i),
symbol: "AAPL".to_string(),
side: OrderSide::Buy as i32,
order_type: OrderType::Market as i32,
quantity: 100.0,
price: 150.0,
status: OrderStatus::New as i32,
filled_quantity: 0.0,
remaining_quantity: 100.0,
average_price: 0.0,
created_time_unix_nanos: 1640995200000000000,
updated_time_unix_nanos: 1640995200000000000,
})
.collect();
let chunk_size = orders.len() / tc;
let tasks = orders.chunks(chunk_size).map(|chunk| {
let chunk = chunk.to_vec();
tokio::spawn(async move {
let mut encoded = Vec::new();
for order in chunk {
let mut buf = Vec::new();
order.encode(&mut buf).unwrap();
encoded.push(buf);
}
encoded
})
});
let results = futures::future::join_all(tasks).await;
black_box(results);
})
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_protobuf_serialization,
bench_json_serialization,
bench_data_transformations,
bench_large_data_structures,
bench_streaming_serialization,
bench_memory_efficiency,
bench_concurrent_serialization
);
criterion_main!(benches);
*/
// Placeholder to keep file valid
fn main() {
println!("Benchmark disabled - see file header for details");
}