pub mod benchmark;
pub mod metrics;
pub mod profiler;
use crate::cli::error::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
pub elapsed_time: Duration,
pub memory_usage: MemoryMetrics,
pub storage_metrics: StorageMetrics,
pub search_metrics: SearchMetrics,
pub operation_count: usize,
pub ops_per_second: f64,
pub peak_memory_mb: f64,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryMetrics {
pub initial_mb: f64,
pub final_mb: f64,
pub peak_mb: f64,
pub allocated_mb: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageMetrics {
pub reads: usize,
pub writes: usize,
pub read_time: Duration,
pub write_time: Duration,
pub avg_read_latency: Duration,
pub avg_write_latency: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchMetrics {
pub search_count: usize,
pub total_search_time: Duration,
pub avg_search_latency: Duration,
pub index_cache_hits: usize,
pub index_cache_misses: usize,
pub cache_hit_ratio: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfilingResult {
pub scenario_name: String,
pub dataset_size: usize,
pub metrics: PerformanceMetrics,
pub metadata: HashMap<String, String>,
pub config: ProfilingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfilingConfig {
pub operation_count: usize,
pub dataset_size: usize,
pub operation_types: Vec<OperationType>,
pub profile_memory: bool,
pub profile_storage: bool,
pub warmup: bool,
pub warmup_iterations: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OperationType {
HistoryStore,
HistorySearch,
FullTextSearch,
IndexOptimization,
SessionManagement,
BatchOperations,
}
impl Default for ProfilingConfig {
fn default() -> Self {
Self {
operation_count: 1000,
dataset_size: 10000,
operation_types: vec![
OperationType::HistoryStore,
OperationType::HistorySearch,
OperationType::FullTextSearch,
],
profile_memory: true,
profile_storage: true,
warmup: true,
warmup_iterations: 100,
}
}
}
impl Default for MemoryMetrics {
fn default() -> Self {
Self {
initial_mb: 0.0,
final_mb: 0.0,
peak_mb: 0.0,
allocated_mb: 0.0,
}
}
}
impl Default for StorageMetrics {
fn default() -> Self {
Self {
reads: 0,
writes: 0,
read_time: Duration::ZERO,
write_time: Duration::ZERO,
avg_read_latency: Duration::ZERO,
avg_write_latency: Duration::ZERO,
}
}
}
impl Default for SearchMetrics {
fn default() -> Self {
Self {
search_count: 0,
total_search_time: Duration::ZERO,
avg_search_latency: Duration::ZERO,
index_cache_hits: 0,
index_cache_misses: 0,
cache_hit_ratio: 0.0,
}
}
}
impl PerformanceMetrics {
pub fn new() -> Self {
Self {
elapsed_time: Duration::ZERO,
memory_usage: MemoryMetrics::default(),
storage_metrics: StorageMetrics::default(),
search_metrics: SearchMetrics::default(),
operation_count: 0,
ops_per_second: 0.0,
peak_memory_mb: 0.0,
timestamp: Utc::now(),
}
}
pub fn calculate_ops_per_second(&mut self) {
if self.elapsed_time.as_secs_f64() > 0.0 {
self.ops_per_second = self.operation_count as f64 / self.elapsed_time.as_secs_f64();
}
}
pub fn calculate_storage_averages(&mut self) {
if self.storage_metrics.reads > 0 {
self.storage_metrics.avg_read_latency =
self.storage_metrics.read_time / self.storage_metrics.reads as u32;
}
if self.storage_metrics.writes > 0 {
self.storage_metrics.avg_write_latency =
self.storage_metrics.write_time / self.storage_metrics.writes as u32;
}
}
pub fn calculate_search_averages(&mut self) {
if self.search_metrics.search_count > 0 {
self.search_metrics.avg_search_latency =
self.search_metrics.total_search_time / self.search_metrics.search_count as u32;
}
let total_cache_operations =
self.search_metrics.index_cache_hits + self.search_metrics.index_cache_misses;
if total_cache_operations > 0 {
self.search_metrics.cache_hit_ratio =
self.search_metrics.index_cache_hits as f64 / total_cache_operations as f64;
}
}
}
impl ProfilingResult {
pub fn generate_summary(&self) -> String {
format!(
"Profiling Results: {}\n\
Dataset Size: {} entries\n\
Operations: {}\n\
Total Time: {:?}\n\
Ops/Second: {:.2}\n\
Peak Memory: {:.2} MB\n\
Storage Reads: {} (avg: {:?})\n\
Storage Writes: {} (avg: {:?})\n\
Search Operations: {} (avg: {:?})\n\
Cache Hit Ratio: {:.2}%",
self.scenario_name,
self.dataset_size,
self.metrics.operation_count,
self.metrics.elapsed_time,
self.metrics.ops_per_second,
self.metrics.peak_memory_mb,
self.metrics.storage_metrics.reads,
self.metrics.storage_metrics.avg_read_latency,
self.metrics.storage_metrics.writes,
self.metrics.storage_metrics.avg_write_latency,
self.metrics.search_metrics.search_count,
self.metrics.search_metrics.avg_search_latency,
self.metrics.search_metrics.cache_hit_ratio * 100.0
)
}
pub async fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
let content = serde_json::to_string_pretty(self)?;
tokio::fs::write(path, content).await?;
Ok(())
}
pub async fn load_from_file(path: &std::path::Path) -> Result<Self> {
let content = tokio::fs::read_to_string(path).await?;
let result: ProfilingResult = serde_json::from_str(&content)?;
Ok(result)
}
}
pub fn get_memory_usage_mb() -> f64 {
0.0 }
pub struct Timer {
start: Instant,
name: String,
}
impl Timer {
pub fn start(name: &str) -> Self {
Self {
start: Instant::now(),
name: name.to_string(),
}
}
pub fn stop(self) -> Duration {
let elapsed = self.start.elapsed();
elapsed
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
}