//! Memory benchmark for SubagentInfo Arc optimization. //! //! This benchmark measures the memory savings from using Arc instead of String //! for SubagentInfo fields. It creates many SubagentInfo instances with shared string //! values and measures the memory usage. //! //! Run with: cargo run --release --example memory_benchmark // Illustrative mock structs whose fields exist to model memory layout; not all // are read back, which is expected for a microbenchmark. #![allow(dead_code)] use std::sync::Arc; use std::time::Instant; /// Simulate SubagentInfo with String fields (before optimization) #[derive(Clone)] struct SubagentInfoString { subagent_id: String, child_session_id: String, description: String, subagent_type: String, persona: Option, role: Option, model: Option, status: Option, tools_used: Vec, } /// Simulate SubagentInfo with Arc fields (after optimization) #[derive(Clone)] struct SubagentInfoArc { subagent_id: Arc, child_session_id: Arc, description: Arc, subagent_type: Arc, persona: Option>, role: Option>, model: Option>, status: Option>, tools_used: Vec>, } fn create_string_info(id: usize) -> SubagentInfoString { SubagentInfoString { subagent_id: format!("sa-{}", id), child_session_id: format!("cs-{}", id), description: "Find API endpoints in the codebase".to_string(), subagent_type: "general-purpose".to_string(), persona: Some("researcher".to_string()), role: Some("analyst".to_string()), model: Some("grok-3".to_string()), status: Some("completed".to_string()), tools_used: vec!["read".to_string(), "search".to_string(), "edit".to_string()], } } fn create_arc_info(id: usize) -> SubagentInfoArc { SubagentInfoArc { subagent_id: Arc::from(format!("sa-{}", id)), child_session_id: Arc::from(format!("cs-{}", id)), description: Arc::from("Find API endpoints in the codebase"), subagent_type: Arc::from("general-purpose"), persona: Some(Arc::from("researcher")), role: Some(Arc::from("analyst")), model: Some(Arc::from("grok-3")), status: Some(Arc::from("completed")), tools_used: vec![Arc::from("read"), Arc::from("search"), Arc::from("edit")], } } fn estimate_string_info_size(info: &SubagentInfoString) -> usize { std::mem::size_of::() + info.subagent_id.capacity() + info.child_session_id.capacity() + info.description.capacity() + info.subagent_type.capacity() + info.persona.as_ref().map_or(0, |s| s.capacity()) + info.role.as_ref().map_or(0, |s| s.capacity()) + info.model.as_ref().map_or(0, |s| s.capacity()) + info.status.as_ref().map_or(0, |s| s.capacity()) + info.tools_used.iter().map(|s| s.capacity()).sum::() } fn estimate_arc_info_size(info: &SubagentInfoArc) -> usize { // Arc has 16 bytes overhead (fat pointer) but shares the string data std::mem::size_of::() + 16 // subagent_id Arc overhead + 16 // child_session_id Arc overhead + 16 // description Arc overhead + 16 // subagent_type Arc overhead + info.persona.as_ref().map_or(0, |_| 16) + info.role.as_ref().map_or(0, |_| 16) + info.model.as_ref().map_or(0, |_| 16) + info.status.as_ref().map_or(0, |_| 16) + info.tools_used.len() * 16 } fn main() { println!("=== SubagentInfo Memory Benchmark ===\n"); // Test 1: Single instance println!("--- Single Instance ---"); let string_info = create_string_info(1); let arc_info = create_arc_info(1); println!( "String-based SubagentInfo size: ~{} bytes", estimate_string_info_size(&string_info) ); println!( "Arc-based SubagentInfo size: ~{} bytes", estimate_arc_info_size(&arc_info) ); println!(); // Test 2: Many instances with shared strings println!("--- 1000 Instances (with string sharing potential) ---"); let count = 1000; // String-based: each instance has its own copy of shared strings let string_infos: Vec = (0..count).map(create_string_info).collect(); // Arc-based: shared strings are deduplicated let arc_infos: Vec = (0..count).map(create_arc_info).collect(); let string_total: usize = string_infos.iter().map(estimate_string_info_size).sum(); let arc_total: usize = arc_infos.iter().map(estimate_arc_info_size).sum(); println!( "String-based total: {} bytes ({:.1} KB)", string_total, string_total as f64 / 1024.0 ); println!( "Arc-based total: {} bytes ({:.1} KB)", arc_total, arc_total as f64 / 1024.0 ); println!( "Savings: {} bytes ({:.1} KB, {:.1}%)", string_total - arc_total, (string_total - arc_total) as f64 / 1024.0, (string_total - arc_total) as f64 / string_total as f64 * 100.0 ); println!(); // Test 3: Clone performance println!("--- Clone Performance (100,000 clones) ---"); let iterations = 100_000; let start = Instant::now(); for _ in 0..iterations { let _ = string_info.clone(); } let string_clone_time = start.elapsed(); let start = Instant::now(); for _ in 0..iterations { let _ = arc_info.clone(); } let arc_clone_time = start.elapsed(); println!("String clone time: {:?}", string_clone_time); println!("Arc clone time: {:?}", arc_clone_time); println!( "Speedup: {:.1}x", string_clone_time.as_nanos() as f64 / arc_clone_time.as_nanos() as f64 ); println!(); // Test 4: Memory with realistic subagent data println!("--- Realistic Scenario (100 subagents with varied data) ---"); let subagent_types = ["general-purpose", "explore", "plan", "implementer"]; let personas = ["researcher", "analyst", "reviewer", "implementer"]; let models = ["grok-3", "grok-3-mini", "grok-4"]; let statuses = ["completed", "failed", "running", "cancelled"]; let tools = ["read", "edit", "search", "execute", "list_dir"]; let mut string_infos: Vec = Vec::new(); let mut arc_infos: Vec = Vec::new(); for i in 0..100 { let st = subagent_types[i % subagent_types.len()]; let p = personas[i % personas.len()]; let m = models[i % models.len()]; let s = statuses[i % statuses.len()]; string_infos.push(SubagentInfoString { subagent_id: format!("sa-{}", i), child_session_id: format!("cs-{}", i), description: format!("Task {}: analyze the codebase", i), subagent_type: st.to_string(), persona: Some(p.to_string()), role: Some(p.to_string()), model: Some(m.to_string()), status: Some(s.to_string()), tools_used: tools .iter() .take(i % 5 + 1) .map(|t| t.to_string()) .collect(), }); arc_infos.push(SubagentInfoArc { subagent_id: Arc::from(format!("sa-{}", i)), child_session_id: Arc::from(format!("cs-{}", i)), description: Arc::from(format!("Task {}: analyze the codebase", i)), subagent_type: Arc::from(st), persona: Some(Arc::from(p)), role: Some(Arc::from(p)), model: Some(Arc::from(m)), status: Some(Arc::from(s)), tools_used: tools .iter() .take(i % 5 + 1) .map(|t| Arc::from(*t)) .collect(), }); } let string_total: usize = string_infos.iter().map(estimate_string_info_size).sum(); let arc_total: usize = arc_infos.iter().map(estimate_arc_info_size).sum(); println!( "String-based total: {} bytes ({:.1} KB)", string_total, string_total as f64 / 1024.0 ); println!( "Arc-based total: {} bytes ({:.1} KB)", arc_total, arc_total as f64 / 1024.0 ); println!( "Savings: {} bytes ({:.1} KB, {:.1}%)", string_total - arc_total, (string_total - arc_total) as f64 / 1024.0, (string_total - arc_total) as f64 / string_total as f64 * 100.0 ); println!(); // Summary println!("=== Summary ==="); println!("Arc optimization provides:"); println!("1. Memory savings through string deduplication (shared strings stored once)"); println!("2. Faster cloning (O(1) refcount increment vs O(n) string copy)"); println!("3. Better cache locality for frequently accessed shared strings"); }