use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId, Throughput};
use futures;
use rand;
use serde_json::{json, Value};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::runtime::Runtime;
use tokio::sync::RwLock;
use bevy_debugger_mcp::{
config::Config,
brp_client::BrpClient,
mcp_server::McpServer,
lazy_init::LazyComponents,
};
#[cfg(feature = "caching")]
use bevy_debugger_mcp::command_cache::{CommandCache, CacheConfig};
#[cfg(feature = "pooling")]
use bevy_debugger_mcp::response_pool::{ResponsePool, ResponsePoolConfig};
struct OptimizationBenchConfig {
runtime: Runtime,
server_baseline: McpServer,
server_optimized: McpServer,
}
impl OptimizationBenchConfig {
fn new() -> Self {
let runtime = Runtime::new().unwrap();
let config = Config {
bevy_brp_host: "localhost".to_string(),
bevy_brp_port: 15702,
mcp_port: 3001,
};
let brp_client_baseline = Arc::new(RwLock::new(BrpClient::new(&config)));
let server_baseline = McpServer::new(config.clone(), brp_client_baseline);
let brp_client_optimized = Arc::new(RwLock::new(BrpClient::new(&config)));
let server_optimized = McpServer::new(config, brp_client_optimized);
Self {
runtime,
server_baseline,
server_optimized,
}
}
}
fn benchmark_initialization_strategies(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let config = Config {
bevy_brp_host: "localhost".to_string(),
bevy_brp_port: 15702,
mcp_port: 3001,
};
let brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
c.bench_function("immediate_initialization", |b| {
b.to_async(&bench_config.runtime).iter_with_setup(
|| {
LazyComponents::new(brp_client.clone())
},
|lazy_components| async {
let _entity_inspector = black_box(lazy_components.get_entity_inspector().await);
let _system_profiler = black_box(lazy_components.get_system_profiler().await);
let _memory_profiler = black_box(lazy_components.get_memory_profiler().await);
let _session_manager = black_box(lazy_components.get_session_manager().await);
let _visual_overlay = black_box(lazy_components.get_visual_debug_overlay().await);
let _debug_router = black_box(lazy_components.get_debug_command_router().await);
},
);
});
c.bench_function("lazy_initialization", |b| {
b.to_async(&bench_config.runtime).iter_with_setup(
|| {
LazyComponents::new(brp_client.clone())
},
|lazy_components| async {
let _entity_inspector = black_box(lazy_components.get_entity_inspector().await);
},
);
});
c.bench_function("lazy_cached_access", |b| {
b.to_async(&bench_config.runtime).iter_with_setup(
|| {
let runtime = Runtime::new().unwrap();
let lazy_components = LazyComponents::new(brp_client.clone());
runtime.block_on(async {
let _inspector = lazy_components.get_entity_inspector().await;
});
lazy_components
},
|lazy_components| async {
let _entity_inspector = black_box(lazy_components.get_entity_inspector().await);
},
);
});
}
#[cfg(feature = "caching")]
fn benchmark_caching_effectiveness(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let cache = CommandCache::new(CacheConfig::default());
let test_queries = vec![
("small_query", json!({"query": "entities with Transform"})),
("medium_query", json!({
"query": "entities with (Transform and Mesh)",
"include_data": true
})),
("large_query", json!({
"query": "entities with (Transform and Mesh and Health)",
"include_data": true,
"include_relationships": true,
"recursive_depth": 2
})),
];
for (query_name, query_args) in &test_queries {
c.bench_with_input(
BenchmarkId::new("cache_miss", query_name),
&(query_name, query_args),
|b, (query_name, query_args)| {
b.to_async(&bench_config.runtime).iter_with_setup(
|| {
let fresh_cache = CommandCache::new(CacheConfig::default());
let unique_tool = format!("observe_{}", rand::random::<u64>());
(fresh_cache, unique_tool, (*query_args).clone())
},
|(fresh_cache, tool_name, args)| async {
let cached_result = fresh_cache.get(&tool_name, &args).await;
if cached_result.is_none() {
tokio::time::sleep(Duration::from_micros(100)).await;
let result = json!({"computed": "result", "query": args});
fresh_cache.set(&tool_name, &args, result.clone()).await;
black_box(result)
} else {
black_box(cached_result.unwrap())
}
},
);
},
);
}
for (query_name, query_args) in &test_queries {
c.bench_with_input(
BenchmarkId::new("cache_hit", query_name),
&(query_name, query_args),
|b, (query_name, query_args)| {
b.to_async(&bench_config.runtime).iter_with_setup(
|| {
let runtime = Runtime::new().unwrap();
let tool_name = format!("observe_{}", query_name);
runtime.block_on(async {
let result = json!({"cached": "result", "query": query_args});
cache.set(&tool_name, query_args, result).await;
});
(tool_name, (*query_args).clone())
},
|(tool_name, args)| async {
let result = black_box(cache.get(&tool_name, &args).await);
assert!(result.is_some(), "Cache should have hit");
result
},
);
},
);
}
}
#[cfg(not(feature = "caching"))]
fn benchmark_caching_effectiveness(c: &mut Criterion) {
c.bench_function("caching_disabled", |b| {
b.iter(|| {
black_box(json!({"computed": "directly"}))
});
});
}
#[cfg(feature = "pooling")]
fn benchmark_pooling_effectiveness(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let pool = ResponsePool::new(ResponsePoolConfig::default());
let test_responses = vec![
("tiny", json!({"status": "ok"})),
("small", json!({
"entities": (0..10).map(|i| json!({
"id": i,
"name": format!("Entity{}", i)
})).collect::<Vec<_>>()
})),
("medium", json!({
"entities": (0..100).map(|i| json!({
"id": i,
"name": format!("Entity{}", i),
"position": [i as f32, 0.0, 0.0],
"components": ["Transform", "Mesh"]
})).collect::<Vec<_>>()
})),
("large", json!({
"entities": (0..1000).map(|i| json!({
"id": i,
"name": format!("Entity{}", i),
"position": [i as f32, i as f32 * 2.0, 0.0],
"components": (0..5).map(|j| json!({
"type": format!("Component{}", j),
"data": {"value": i * j, "metadata": format!("meta{}", j)}
})).collect::<Vec<_>>()
})).collect::<Vec<_>>()
})),
("extra_large", json!({
"world_state": {
"entities": (0..2000).map(|i| json!({
"id": i,
"name": format!("Entity{}", i),
"components": (0..8).map(|j| json!({
"type": format!("Component{}", j),
"data": {
"values": (0..10).collect::<Vec<_>>(),
"metadata": format!("Component {} data", j)
}
})).collect::<Vec<_>>()
})).collect::<Vec<_>>(),
"metadata": {"timestamp": 1234567890, "version": "1.0"}
}
})),
];
for (size_name, response) in &test_responses {
let response_size = serde_json::to_string(response).unwrap().len();
c.bench_with_input(
BenchmarkId::new("json_serialize_standard", size_name),
response,
|b, response| {
b.throughput(Throughput::Bytes(response_size as u64));
b.iter(|| {
let _result = black_box(serde_json::to_string(response)).unwrap();
});
},
);
c.bench_with_input(
BenchmarkId::new("json_serialize_pooled", size_name),
response,
|b, response| {
b.throughput(Throughput::Bytes(response_size as u64));
b.to_async(&bench_config.runtime).iter(|| async {
let _result = black_box(pool.serialize_json(response).await).unwrap();
});
},
);
}
}
#[cfg(not(feature = "pooling"))]
fn benchmark_pooling_effectiveness(c: &mut Criterion) {
let response = json!({"entities": (0..100).collect::<Vec<_>>()});
c.bench_function("standard_serialization", |b| {
b.iter(|| {
let _result = black_box(serde_json::to_string(&response)).unwrap();
});
});
}
fn benchmark_e2e_optimization_impact(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let test_operations = vec![
("health_check", json!({})),
("resource_metrics", json!({})),
("observe_simple", json!({"query": "entities with Transform"})),
("observe_complex", json!({
"query": "entities with (Transform and Mesh)",
"include_data": true,
"sort": "name",
"limit": 50
})),
];
for (op_name, args) in test_operations {
c.bench_with_input(
BenchmarkId::new("e2e_baseline", op_name),
&args,
|b, args| {
b.to_async(&bench_config.runtime).iter(|| async {
let _result = black_box(
bench_config.server_baseline.handle_tool_call(op_name, args.clone()).await
);
});
},
);
c.bench_with_input(
BenchmarkId::new("e2e_optimized", op_name),
&args,
|b, args| {
b.to_async(&bench_config.runtime).iter(|| async {
let _result = black_box(
bench_config.server_optimized.handle_tool_call(op_name, args.clone()).await
);
});
},
);
}
}
fn benchmark_concurrent_performance(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let concurrency_levels = [1, 2, 4, 8, 16];
for &concurrency in &concurrency_levels {
c.bench_with_input(
BenchmarkId::new("concurrent_operations", concurrency),
&concurrency,
|b, &concurrency| {
b.to_async(&bench_config.runtime).iter(|| async {
let tasks = (0..concurrency).map(|i| {
let server = &bench_config.server_optimized;
let operation = if i % 2 == 0 { "health_check" } else { "resource_metrics" };
let args = json!({"worker_id": i});
async move {
server.handle_tool_call(operation, args).await
}
});
let results = futures::future::join_all(tasks).await;
black_box(results)
});
},
);
}
}
fn benchmark_memory_efficiency(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let test_scenarios = vec![
("repeated_small_queries", 1000, json!({"query": "single entity"})),
("repeated_medium_queries", 100, json!({
"query": "entities with Transform",
"include_data": true
})),
("repeated_large_queries", 10, json!({
"query": "all entities with full data",
"include_data": true,
"include_relationships": true,
"recursive": true
})),
];
for (scenario_name, iteration_count, query_args) in test_scenarios {
c.bench_with_input(
BenchmarkId::new("memory_efficiency", scenario_name),
&(iteration_count, query_args),
|b, (iteration_count, query_args)| {
b.to_async(&bench_config.runtime).iter(|| async {
for i in 0..iteration_count {
let args = json!({
"query": query_args.get("query"),
"iteration": i,
"timestamp": std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis()
});
let _result = bench_config.server_optimized
.handle_tool_call("observe", args)
.await;
if i % 100 == 0 {
tokio::time::sleep(Duration::from_micros(1)).await;
}
}
});
},
);
}
}
fn benchmark_stress_degradation(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let stress_levels = vec![
("light_stress", 10, 10), ("medium_stress", 50, 5), ("heavy_stress", 100, 1), ("extreme_stress", 200, 0), ];
for (stress_name, op_count, delay_ms) in stress_levels {
c.bench_with_input(
BenchmarkId::new("stress_test", stress_name),
&(op_count, delay_ms),
|b, (op_count, delay_ms)| {
b.to_async(&bench_config.runtime).iter(|| async {
let start_time = Instant::now();
for i in 0..op_count {
let operation = match i % 4 {
0 => "health_check",
1 => "resource_metrics",
2 => "observe",
_ => "diagnostic_report",
};
let args = json!({
"iteration": i,
"stress_level": stress_name
});
let _result = bench_config.server_optimized
.handle_tool_call(operation, args)
.await;
if delay_ms > 0 {
tokio::time::sleep(Duration::from_millis(delay_ms)).await;
}
}
let total_time = start_time.elapsed();
black_box(total_time)
});
},
);
}
}
#[cfg(feature = "caching")]
fn benchmark_cache_pressure(c: &mut Criterion) {
let bench_config = OptimizationBenchConfig::new();
let cache_sizes = [10, 50, 100, 500];
for &max_entries in &cache_sizes {
c.bench_with_input(
BenchmarkId::new("cache_pressure", max_entries),
&max_entries,
|b, &max_entries| {
b.to_async(&bench_config.runtime).iter_with_setup(
|| {
let config = CacheConfig {
max_entries,
default_ttl: Duration::from_secs(300),
cleanup_interval: Duration::from_secs(60),
max_response_size: 1024 * 1024,
};
CommandCache::new(config)
},
|cache| async {
for i in 0..(max_entries * 2) {
let tool_name = format!("tool_{}", i);
let args = json!({"unique_id": i});
let response = json!({"result": format!("data_{}", i)});
cache.set(&tool_name, &args, response).await;
}
let mut hits = 0;
let mut misses = 0;
for i in 0..(max_entries * 2) {
let tool_name = format!("tool_{}", i);
let args = json!({"unique_id": i});
if cache.get(&tool_name, &args).await.is_some() {
hits += 1;
} else {
misses += 1;
}
}
black_box((hits, misses))
},
);
},
);
}
}
#[cfg(not(feature = "caching"))]
fn benchmark_cache_pressure(c: &mut Criterion) {
c.bench_function("cache_pressure_disabled", |b| {
b.iter(|| {
black_box(())
});
});
}
criterion_group!(
optimization_benches,
benchmark_initialization_strategies,
benchmark_caching_effectiveness,
benchmark_pooling_effectiveness,
benchmark_e2e_optimization_impact,
benchmark_concurrent_performance,
benchmark_memory_efficiency,
benchmark_stress_degradation,
benchmark_cache_pressure,
);
criterion_main!(optimization_benches);
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_bevdbg_012_performance_targets() {
let server = {
let config = Config {
bevy_brp_host: "localhost".to_string(),
bevy_brp_port: 15702,
mcp_port: 3001,
};
let brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
McpServer::new(config, brp_client)
};
let mut durations = Vec::new();
for i in 0..100 {
let start = Instant::now();
let _ = server.handle_tool_call("health_check", json!({"test_id": i})).await;
durations.push(start.elapsed());
}
durations.sort();
let p99_duration = durations[98];
println!("P99 latency: {:?}", p99_duration);
assert!(
p99_duration < Duration::from_millis(10),
"P99 command processing should be < 10ms in test environment, got {:?}",
p99_duration
);
let concurrent_tasks = (0..10).map(|i| {
let server = &server;
async move {
let start = Instant::now();
let _ = server.handle_tool_call("observe", json!({
"query": format!("test query {}", i)
})).await;
start.elapsed()
}
});
let concurrent_results = futures::future::join_all(concurrent_tasks).await;
let max_concurrent_duration = concurrent_results.iter().max().unwrap();
println!("Max concurrent operation duration: {:?}", max_concurrent_duration);
assert!(
*max_concurrent_duration < Duration::from_millis(100),
"Concurrent operations should complete within 100ms, got {:?}",
max_concurrent_duration
);
println!("✅ BEVDBG-012 performance targets validated");
}
#[tokio::test]
async fn test_optimization_baseline_compatibility() {
let server = {
let config = Config {
bevy_brp_host: "localhost".to_string(),
bevy_brp_port: 15702,
mcp_port: 3001,
};
let brp_client = Arc::new(RwLock::new(BrpClient::new(&config)));
McpServer::new(config, brp_client)
};
let operations = [
("health_check", json!({})),
("resource_metrics", json!({})),
("observe", json!({"query": "test"})),
("diagnostic_report", json!({"action": "generate"})),
];
for (op_name, args) in operations {
let result = server.handle_tool_call(op_name, args).await;
println!("Operation '{}' completed: {}", op_name, result.is_ok());
}
println!("✅ Optimization baseline compatibility validated");
}
}