//! Profile results and aggregation. use crate::config::{DeviceType, ModelType}; use crate::metrics::{LatencyMetrics, MemoryMetrics, ThroughputMetrics}; use serde::{Deserialize, Serialize}; /// Single profiling result for a specific configuration. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ProfileResult { /// Model that was profiled pub model_type: ModelType, /// Device used for profiling pub device: DeviceType, /// Batch size tested pub batch_size: usize, /// Latency measurements pub latency: LatencyMetrics, /// Memory usage pub memory: MemoryMetrics, /// Throughput statistics pub throughput: ThroughputMetrics, /// Unix timestamp when profile was captured pub timestamp: u64, } impl ProfileResult { /// Creates a new profile result. #[must_use] pub fn new( model_type: ModelType, device: DeviceType, batch_size: usize, latency: LatencyMetrics, memory: MemoryMetrics, throughput: ThroughputMetrics, timestamp: u64, ) -> Self { Self { model_type, device, batch_size, latency, memory, throughput, timestamp, } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_profile_result_creation() { let latency = LatencyMetrics::from_measurements(&[10.0, 20.0, 30.0]).unwrap(); let memory = MemoryMetrics::new(100.0, 80.0, 120.0).unwrap(); let throughput = ThroughputMetrics::new(50.0, None).unwrap(); let result = ProfileResult::new( ModelType::ResNet18, DeviceType::CPU, 8, latency, memory, throughput, 1234567890, ); assert_eq!(result.model_type, ModelType::ResNet18); assert_eq!(result.device, DeviceType::CPU); assert_eq!(result.batch_size, 8); assert_eq!(result.timestamp, 1234567890); } #[test] fn test_profile_result_serialization() { let latency = LatencyMetrics::from_measurements(&[10.0, 20.0, 30.0]).unwrap(); let memory = MemoryMetrics::new(100.0, 80.0, 120.0).unwrap(); let throughput = ThroughputMetrics::new(50.0, None).unwrap(); let result = ProfileResult::new( ModelType::ViTB16, DeviceType::CUDA, 16, latency, memory, throughput, 1234567890, ); let json = serde_json::to_string(&result).expect("serialization failed"); assert!(json.contains("\"model_type\"")); assert!(json.contains("\"vit-b16\"")); assert!(json.contains("\"batch_size\"")); assert!(json.contains("\"latency\"")); assert!(json.contains("\"memory\"")); assert!(json.contains("\"throughput\"")); } #[test] fn test_profile_result_deserialization() { let json = r#"{ "model_type": "resnet50", "device": "CUDA", "batch_size": 32, "latency": { "mean_ms": 20.0, "std_ms": 2.0, "min_ms": 18.0, "max_ms": 25.0, "p50_ms": 20.0, "p95_ms": 23.0, "p99_ms": 24.0 }, "memory": { "peak_memory_mb": 200.0, "allocated_mb": 180.0, "reserved_mb": 220.0 }, "throughput": { "samples_per_sec": 1600.0, "tokens_per_sec": null }, "timestamp": 1234567890 }"#; let result: ProfileResult = serde_json::from_str(json).expect("deserialization failed"); assert_eq!(result.model_type, ModelType::ResNet50); assert_eq!(result.device, DeviceType::CUDA); assert_eq!(result.batch_size, 32); } #[test] fn test_profile_result_roundtrip() { let latency = LatencyMetrics::from_measurements(&[10.0, 20.0, 30.0]).unwrap(); let memory = MemoryMetrics::new(100.0, 80.0, 120.0).unwrap(); let throughput = ThroughputMetrics::new(50.0, Some(200.0)).unwrap(); let original = ProfileResult::new( ModelType::ConvNeXtBase, DeviceType::Metal, 4, latency, memory, throughput, 9876543210, ); let json = serde_json::to_string(&original).expect("serialization failed"); let decoded: ProfileResult = serde_json::from_str(&json).expect("deserialization failed"); assert_eq!(decoded.model_type, original.model_type); assert_eq!(decoded.device, original.device); assert_eq!(decoded.batch_size, original.batch_size); assert_eq!(decoded.timestamp, original.timestamp); } }