sql_cli/benchmarks/
metrics.rs

1use std::time::{Duration, Instant};
2
3#[derive(Debug, Clone)]
4pub struct BenchmarkMetrics {
5    pub query_parse_time: Duration,
6    pub query_plan_time: Duration,
7    pub query_execute_time: Duration,
8    pub total_time: Duration,
9
10    pub rows_processed: usize,
11    pub rows_returned: usize,
12    pub rows_per_second: f64,
13
14    pub peak_memory_bytes: Option<usize>,
15    pub allocations_count: Option<usize>,
16
17    pub table_scans: usize,
18    pub expressions_evaluated: usize,
19    pub functions_called: usize,
20}
21
22impl BenchmarkMetrics {
23    pub fn new() -> Self {
24        BenchmarkMetrics {
25            query_parse_time: Duration::ZERO,
26            query_plan_time: Duration::ZERO,
27            query_execute_time: Duration::ZERO,
28            total_time: Duration::ZERO,
29            rows_processed: 0,
30            rows_returned: 0,
31            rows_per_second: 0.0,
32            peak_memory_bytes: None,
33            allocations_count: None,
34            table_scans: 0,
35            expressions_evaluated: 0,
36            functions_called: 0,
37        }
38    }
39
40    pub fn calculate_throughput(&mut self) {
41        if self.total_time.as_secs_f64() > 0.0 {
42            self.rows_per_second = self.rows_processed as f64 / self.total_time.as_secs_f64();
43        }
44    }
45
46    pub fn to_csv_row(&self) -> String {
47        format!(
48            "{:.3},{:.3},{:.3},{:.3},{},{},{:.0}",
49            self.query_parse_time.as_secs_f64() * 1000.0,
50            self.query_plan_time.as_secs_f64() * 1000.0,
51            self.query_execute_time.as_secs_f64() * 1000.0,
52            self.total_time.as_secs_f64() * 1000.0,
53            self.rows_processed,
54            self.rows_returned,
55            self.rows_per_second
56        )
57    }
58}
59
60#[derive(Debug)]
61pub struct BenchmarkResult {
62    pub query_name: String,
63    pub query_category: String,
64    pub table_name: String,
65    pub row_count: usize,
66    pub metrics: BenchmarkMetrics,
67    pub error: Option<String>,
68}
69
70impl BenchmarkResult {
71    pub fn new(
72        query_name: String,
73        query_category: String,
74        table_name: String,
75        row_count: usize,
76    ) -> Self {
77        BenchmarkResult {
78            query_name,
79            query_category,
80            table_name,
81            row_count,
82            metrics: BenchmarkMetrics::new(),
83            error: None,
84        }
85    }
86
87    pub fn to_csv_row(&self) -> String {
88        format!(
89            "{},{},{},{},{}",
90            self.query_name,
91            self.table_name,
92            self.row_count,
93            self.metrics.to_csv_row(),
94            self.error.as_ref().unwrap_or(&"OK".to_string())
95        )
96    }
97}
98
99pub struct MetricsCollector {
100    start: Option<Instant>,
101    phase_start: Option<Instant>,
102    current_metrics: BenchmarkMetrics,
103}
104
105impl MetricsCollector {
106    pub fn new() -> Self {
107        MetricsCollector {
108            start: None,
109            phase_start: None,
110            current_metrics: BenchmarkMetrics::new(),
111        }
112    }
113
114    pub fn start_total(&mut self) {
115        self.start = Some(Instant::now());
116    }
117
118    pub fn start_phase(&mut self) {
119        self.phase_start = Some(Instant::now());
120    }
121
122    pub fn end_parse_phase(&mut self) {
123        if let Some(start) = self.phase_start {
124            self.current_metrics.query_parse_time = start.elapsed();
125        }
126    }
127
128    pub fn end_plan_phase(&mut self) {
129        if let Some(start) = self.phase_start {
130            self.current_metrics.query_plan_time = start.elapsed();
131        }
132    }
133
134    pub fn end_execute_phase(&mut self) {
135        if let Some(start) = self.phase_start {
136            self.current_metrics.query_execute_time = start.elapsed();
137        }
138    }
139
140    pub fn end_total(&mut self) {
141        if let Some(start) = self.start {
142            self.current_metrics.total_time = start.elapsed();
143        }
144    }
145
146    pub fn set_rows(&mut self, processed: usize, returned: usize) {
147        self.current_metrics.rows_processed = processed;
148        self.current_metrics.rows_returned = returned;
149        self.current_metrics.calculate_throughput();
150    }
151
152    pub fn get_metrics(&self) -> BenchmarkMetrics {
153        self.current_metrics.clone()
154    }
155}