use crate ::measurement ::BenchmarkResult;
use std ::time ::Duration;
use std ::collections ::HashMap;
#[ derive(Debug, Clone) ]
pub struct ParserAnalyzer
{
pub parser_name: String,
pub command_count: u64,
pub character_count: u64,
pub average_complexity: f64,
}
impl ParserAnalyzer
{
pub fn new(parser_name: impl Into< String >, command_count: u64, character_count: u64) -> Self
{
Self
{
parser_name: parser_name.into(),
command_count,
character_count,
average_complexity: 1.0,
}
}
pub fn with_complexity(mut self, complexity: f64) -> Self
{
self.average_complexity = complexity;
self
}
pub fn analyze(&self, result: &BenchmarkResult) -> ParserMetrics
{
let mean_duration = result.mean_time();
let mean_seconds = mean_duration.as_secs_f64();
let commands_per_second = if mean_seconds > 0.0
{
self.command_count as f64 / mean_seconds
}
else
{
0.0
};
let characters_per_second = if mean_seconds > 0.0
{
self.character_count as f64 / mean_seconds
}
else
{
0.0
};
let tokens_per_second = commands_per_second * self.average_complexity;
ParserMetrics
{
parser_name: self.parser_name.clone(),
processing_time: mean_duration,
commands_per_second,
characters_per_second,
tokens_per_second,
throughput_mb_per_second: characters_per_second / 1_048_576.0,
command_count: self.command_count,
character_count: self.character_count,
average_complexity: self.average_complexity,
}
}
pub fn compare_parsers(&self, results: &HashMap< String, BenchmarkResult >) -> ParserComparison
{
let mut metrics = HashMap ::new();
for (name, result) in results
{
let parser_metrics = self.analyze(result);
metrics.insert(name.clone(), parser_metrics);
}
ParserComparison
{
parser_name: self.parser_name.clone(),
metrics,
}
}
}
#[ derive(Debug, Clone) ]
pub struct ParserMetrics
{
pub parser_name: String,
pub processing_time: Duration,
pub commands_per_second: f64,
pub characters_per_second: f64,
pub tokens_per_second: f64,
pub throughput_mb_per_second: f64,
pub command_count: u64,
pub character_count: u64,
pub average_complexity: f64,
}
impl ParserMetrics
{
pub fn commands_description( &self ) -> String
{
if self.commands_per_second >= 1_000_000.0
{
format!("{:.1}M cmd/s", self.commands_per_second / 1_000_000.0)
}
else if self.commands_per_second >= 1_000.0
{
format!("{:.1}K cmd/s", self.commands_per_second / 1_000.0)
}
else
{
format!("{:.0} cmd/s", self.commands_per_second)
}
}
pub fn tokens_description( &self ) -> String
{
if self.tokens_per_second >= 1_000_000.0
{
format!("{:.1}M tokens/s", self.tokens_per_second / 1_000_000.0)
}
else if self.tokens_per_second >= 1_000.0
{
format!("{:.1}K tokens/s", self.tokens_per_second / 1_000.0)
}
else
{
format!("{:.0} tokens/s", self.tokens_per_second)
}
}
pub fn throughput_description( &self ) -> String
{
if self.throughput_mb_per_second >= 1.0
{
format!("{:.1} MB/s", self.throughput_mb_per_second)
}
else
{
format!("{:.0} KB/s", self.characters_per_second / 1024.0)
}
}
pub fn to_markdown( &self ) -> String
{
let mut report = String ::new();
report.push_str(&format!("### {} Parser Analysis\n\n", self.parser_name));
report.push_str(&format!("- **Commands processed** : {} ({:.1} avg complexity)\n",
self.command_count, self.average_complexity));
report.push_str(&format!("- **Characters processed** : {} ({:.1} chars/cmd)\n",
self.character_count,
self.character_count as f64 / self.command_count as f64));
report.push_str(&format!("- **Processing time** : {:.3?}\n", self.processing_time));
report.push_str(&format!("- **Performance** : \n"));
report.push_str(&format!(" - Commands: {}\n", self.commands_description()));
report.push_str(&format!(" - Tokens: {}\n", self.tokens_description()));
report.push_str(&format!(" - Throughput: {}\n", self.throughput_description()));
report.push('\n');
report
}
}
#[ derive(Debug, Clone) ]
pub struct ParserComparison
{
pub parser_name: String,
pub metrics: HashMap< String, ParserMetrics >,
}
impl ParserComparison
{
pub fn fastest_parser( &self ) -> Option< (&String, &ParserMetrics) >
{
self.metrics
.iter()
.max_by(|a, b| a.1.commands_per_second.partial_cmp(&b.1.commands_per_second).unwrap())
}
pub fn highest_throughput( &self ) -> Option< (&String, &ParserMetrics) >
{
self.metrics
.iter()
.max_by(|a, b| a.1.throughput_mb_per_second.partial_cmp(&b.1.throughput_mb_per_second).unwrap())
}
pub fn calculate_speedups(&self, baseline: &str) -> Option< HashMap<String, f64 >>
{
let baseline_rate = self.metrics.get(baseline)?.commands_per_second;
if baseline_rate <= 0.0
{
return None;
}
let mut speedups = HashMap ::new();
for (name, metrics) in &self.metrics
{
let speedup = metrics.commands_per_second / baseline_rate;
speedups.insert(name.clone(), speedup);
}
Some(speedups)
}
pub fn to_markdown( &self ) -> String
{
let mut report = String ::new();
report.push_str(&format!("## {} Parser Comparison\n\n", self.parser_name));
if let Some((fastest_name, fastest_metrics)) = self.fastest_parser()
{
report.push_str(&format!("**Best performing parser** : {} ({})\n\n",
fastest_name,
fastest_metrics.commands_description()));
}
report.push_str("| Implementation | Commands/sec | Tokens/sec | Throughput | Avg Latency | Complexity |\n");
report.push_str("|----------------|--------------|------------|------------|-------------|------------|\n");
let mut sorted_metrics: Vec< _ > = self.metrics.iter().collect();
sorted_metrics.sort_by(|a, b| b.1.commands_per_second.partial_cmp(&a.1.commands_per_second).unwrap());
for (name, metrics) in &sorted_metrics
{
let avg_latency = if metrics.commands_per_second > 0.0
{
format!("{:.1} μs", 1_000_000.0 / metrics.commands_per_second)
}
else
{
"N/A".to_string()
};
report.push_str(&format!(
"| {} | {} | {} | {} | {} | {:.1} |\n",
name,
metrics.commands_description(),
metrics.tokens_description(),
metrics.throughput_description(),
avg_latency,
metrics.average_complexity
));
}
report.push('\n');
if self.metrics.len() > 1
{
let slowest_name = sorted_metrics.last().unwrap().0;
if let Some(speedups) = self.calculate_speedups(slowest_name)
{
report.push_str("### Performance Speedups\n\n");
report.push_str(&format!("*Relative to {} (baseline)*\n\n", slowest_name));
for (name, _metrics) in &sorted_metrics
{
if let Some(speedup) = speedups.get(*name)
{
if *name != slowest_name
{
report.push_str(&format!("- **{}** : {:.1}x faster\n", name, speedup));
}
}
}
report.push('\n');
}
}
report
}
}
#[ derive(Debug, Clone) ]
pub struct ParserPipelineAnalyzer
{
stage_names: Vec< String >,
stage_results: HashMap< String, BenchmarkResult >,
}
impl ParserPipelineAnalyzer
{
pub fn new() -> Self
{
Self
{
stage_names: Vec ::new(),
stage_results: HashMap ::new(),
}
}
pub fn add_stage(&mut self, name: impl Into< String >, result: BenchmarkResult) -> &mut Self
{
let stage_name = name.into();
self.stage_names.push(stage_name.clone());
self.stage_results.insert(stage_name, result);
self
}
pub fn analyze_bottlenecks( &self ) -> PipelineAnalysis
{
let mut stage_times = HashMap ::new();
let mut total_time = Duration ::ZERO;
for (name, result) in &self.stage_results
{
let mean_time = result.mean_time();
stage_times.insert(name.clone(), mean_time);
total_time += mean_time;
}
let bottleneck = stage_times
.iter()
.max_by(|a, b| a.1.cmp(b.1))
.map(|(name, time)| (name.clone(), *time));
let mut stage_percentages = HashMap ::new();
if total_time > Duration ::ZERO
{
for (name, time) in &stage_times
{
let percentage = time.as_secs_f64() / total_time.as_secs_f64() * 100.0;
stage_percentages.insert(name.clone(), percentage);
}
}
PipelineAnalysis
{
stage_times,
stage_percentages,
bottleneck,
total_time,
stage_count: self.stage_results.len(),
}
}
pub fn to_markdown( &self ) -> String
{
let analysis = self.analyze_bottlenecks();
let mut report = String ::new();
report.push_str("## Parser Pipeline Analysis\n\n");
if let Some((bottleneck_name, bottleneck_time)) = &analysis.bottleneck
{
report.push_str(&format!("**Primary bottleneck** : {} ({:.2?})\n",
bottleneck_name, bottleneck_time));
if let Some(percentage) = analysis.stage_percentages.get(bottleneck_name)
{
report.push_str(&format!("**Bottleneck impact** : {:.1}% of total processing time\n\n", percentage));
}
}
report.push_str("| Pipeline Stage | Time | Percentage | Reliability |\n");
report.push_str("|----------------|------|------------|-------------|\n");
for stage_name in &self.stage_names
{
if let (Some(time), Some(percentage), Some(result)) = (
analysis.stage_times.get(stage_name),
analysis.stage_percentages.get(stage_name),
self.stage_results.get(stage_name)
) {
let reliability = if result.is_reliable() { "✅ Reliable" } else { "⚠️ Variable" };
report.push_str(&format!(
"| {} | {:.2?} | {:.1}% | {} |\n",
stage_name, time, percentage, reliability
));
}
}
report.push('\n');
report.push_str("### Optimization Recommendations\n\n");
if let Some((bottleneck_name, _)) = &analysis.bottleneck
{
if let Some(percentage) = analysis.stage_percentages.get(bottleneck_name)
{
if *percentage > 50.0
{
report.push_str(&format!("🎯 **High Priority** : Optimize {} stage ({:.1}% of total time)\n",
bottleneck_name, percentage));
}
else if *percentage > 25.0
{
report.push_str(&format!("⚡ **Medium Priority** : Consider optimizing {} stage ({:.1}% of total time)\n",
bottleneck_name, percentage));
}
}
}
for (stage_name, result) in &self.stage_results
{
if !result.is_reliable()
{
let cv = result.coefficient_of_variation() * 100.0;
report.push_str(&format!("📊 **Reliability Issue** : {} stage has high variability (CV: {:.1}%)\n",
stage_name, cv));
}
}
report.push('\n');
report
}
}
#[ derive(Debug, Clone) ]
pub struct PipelineAnalysis
{
pub stage_times: HashMap< String, Duration >,
pub stage_percentages: HashMap< String, f64 >,
pub bottleneck: Option< (String, Duration) >,
pub total_time: Duration,
pub stage_count: usize,
}
impl Default for ParserPipelineAnalyzer
{
fn default() -> Self
{
Self ::new()
}
}