async_inspect/profile/
reporter.rs1use super::Profiler;
4use std::fmt::Write as FmtWrite;
5
6pub struct PerformanceReporter<'a> {
8 profiler: &'a Profiler,
9}
10
11impl<'a> PerformanceReporter<'a> {
12 #[must_use]
14 pub fn new(profiler: &'a Profiler) -> Self {
15 Self { profiler }
16 }
17
18 pub fn print_report(&self) {
20 self.print_header();
21 self.print_overall_stats();
22 self.print_bottlenecks();
23 self.print_hot_paths();
24 self.print_slowest_tasks();
25 self.print_await_stats();
26 self.print_efficiency_analysis();
27 }
28
29 fn print_header(&self) {
31 println!("\n╔════════════════════════════════════════════════════════════╗");
32 println!("║ async-inspect - Performance Report ║");
33 println!("╚════════════════════════════════════════════════════════════╝\n");
34 }
35
36 fn print_overall_stats(&self) {
38 println!("┌────────────────────────────────────────────────────────────┐");
39 println!("│ Overall Statistics │");
40 println!("└────────────────────────────────────────────────────────────┘");
41
42 let stats = self.profiler.calculate_stats();
43 let all_metrics = self.profiler.all_metrics();
44
45 println!(" Total Tasks: {}", all_metrics.len());
46 println!(
47 " Completed: {}",
48 all_metrics.iter().filter(|m| m.completed).count()
49 );
50 println!();
51 println!(" Duration Stats:");
52 println!(
53 " Min: {:.2}ms",
54 stats.min.as_secs_f64() * 1000.0
55 );
56 println!(
57 " Max: {:.2}ms",
58 stats.max.as_secs_f64() * 1000.0
59 );
60 println!(
61 " Mean: {:.2}ms",
62 stats.mean.as_secs_f64() * 1000.0
63 );
64 println!(
65 " Median (p50): {:.2}ms",
66 stats.median.as_secs_f64() * 1000.0
67 );
68 println!(
69 " p95: {:.2}ms",
70 stats.p95.as_secs_f64() * 1000.0
71 );
72 println!(
73 " p99: {:.2}ms",
74 stats.p99.as_secs_f64() * 1000.0
75 );
76 println!(" Std Dev: {:.2}ms", stats.std_dev * 1000.0);
77 println!();
78 }
79
80 fn print_bottlenecks(&self) {
82 let bottlenecks = self.profiler.identify_bottlenecks();
83
84 println!("┌────────────────────────────────────────────────────────────┐");
85 println!("│ Bottleneck Analysis │");
86 println!("└────────────────────────────────────────────────────────────┘");
87
88 if bottlenecks.is_empty() {
89 println!(" ✅ No bottlenecks detected\n");
90 return;
91 }
92
93 println!(
94 " ⚠️ Found {} potential bottleneck(s):\n",
95 bottlenecks.len()
96 );
97
98 for (i, metrics) in bottlenecks.iter().enumerate().take(10) {
99 println!(
100 " {}. {} (#{}) - {:.2}ms",
101 i + 1,
102 metrics.name,
103 metrics.task_id.as_u64(),
104 metrics.total_duration.as_secs_f64() * 1000.0
105 );
106 println!(
107 " Running: {:.2}ms | Blocked: {:.2}ms | Efficiency: {:.1}%",
108 metrics.running_time.as_secs_f64() * 1000.0,
109 metrics.blocked_time.as_secs_f64() * 1000.0,
110 metrics.efficiency() * 100.0
111 );
112 }
113 println!();
114 }
115
116 fn print_hot_paths(&self) {
118 let hot_paths = self.profiler.get_hot_paths();
119
120 println!("┌────────────────────────────────────────────────────────────┐");
121 println!("│ Hot Paths (Most Frequently Executed) │");
122 println!("└────────────────────────────────────────────────────────────┘");
123
124 if hot_paths.is_empty() {
125 println!(" No hot paths identified\n");
126 return;
127 }
128
129 println!(" Top execution paths:\n");
130
131 for (i, path) in hot_paths.iter().enumerate().take(10) {
132 println!(" {}. {} ", i + 1, path.path);
133 println!(
134 " Executions: {} | Total: {:.2}ms | Avg: {:.2}ms",
135 path.execution_count,
136 path.total_time.as_secs_f64() * 1000.0,
137 path.avg_time.as_secs_f64() * 1000.0
138 );
139 }
140 println!();
141 }
142
143 fn print_slowest_tasks(&self) {
145 let slowest = self.profiler.slowest_tasks(10);
146
147 println!("┌────────────────────────────────────────────────────────────┐");
148 println!("│ Slowest Tasks │");
149 println!("└────────────────────────────────────────────────────────────┘");
150
151 if slowest.is_empty() {
152 println!(" No tasks to analyze\n");
153 return;
154 }
155
156 for (i, metrics) in slowest.iter().enumerate() {
157 println!(
158 " {}. {} (#{}) - {:.2}ms",
159 i + 1,
160 metrics.name,
161 metrics.task_id.as_u64(),
162 metrics.total_duration.as_secs_f64() * 1000.0
163 );
164 println!(
165 " Polls: {} | Awaits: {} | Avg poll: {:.2}ms",
166 metrics.poll_count,
167 metrics.await_count,
168 metrics.avg_poll_duration.as_secs_f64() * 1000.0
169 );
170 }
171 println!();
172 }
173
174 fn print_await_stats(&self) {
176 let stats = self.profiler.await_stats();
177
178 println!("┌────────────────────────────────────────────────────────────┐");
179 println!("│ Await Point Analysis │");
180 println!("└────────────────────────────────────────────────────────────┘");
181
182 if stats.count == 0 {
183 println!(" No await points recorded\n");
184 return;
185 }
186
187 println!(" Total Await Points: {}", stats.count);
188 println!();
189 println!(" Await Duration Stats:");
190 println!(
191 " Min: {:.2}ms",
192 stats.min.as_secs_f64() * 1000.0
193 );
194 println!(
195 " Max: {:.2}ms",
196 stats.max.as_secs_f64() * 1000.0
197 );
198 println!(
199 " Mean: {:.2}ms",
200 stats.mean.as_secs_f64() * 1000.0
201 );
202 println!(
203 " Median (p50): {:.2}ms",
204 stats.median.as_secs_f64() * 1000.0
205 );
206 println!(
207 " p95: {:.2}ms",
208 stats.p95.as_secs_f64() * 1000.0
209 );
210 println!(
211 " p99: {:.2}ms",
212 stats.p99.as_secs_f64() * 1000.0
213 );
214 println!();
215 }
216
217 fn print_efficiency_analysis(&self) {
219 let least_efficient = self.profiler.least_efficient_tasks(5);
220
221 println!("┌────────────────────────────────────────────────────────────┐");
222 println!("│ Efficiency Analysis (Least Efficient Tasks) │");
223 println!("└────────────────────────────────────────────────────────────┘");
224
225 if least_efficient.is_empty() {
226 println!(" No tasks to analyze\n");
227 return;
228 }
229
230 println!(" Tasks with highest blocked time ratio:\n");
231
232 for (i, metrics) in least_efficient.iter().enumerate() {
233 let efficiency_pct = metrics.efficiency() * 100.0;
234 let blocked_pct =
235 (metrics.blocked_time.as_secs_f64() / metrics.total_duration.as_secs_f64()) * 100.0;
236
237 println!(
238 " {}. {} (#{}) - {:.1}% efficient",
239 i + 1,
240 metrics.name,
241 metrics.task_id.as_u64(),
242 efficiency_pct
243 );
244 println!(
245 " Total: {:.2}ms | Running: {:.2}ms ({:.1}%) | Blocked: {:.2}ms ({:.1}%)",
246 metrics.total_duration.as_secs_f64() * 1000.0,
247 metrics.running_time.as_secs_f64() * 1000.0,
248 efficiency_pct,
249 metrics.blocked_time.as_secs_f64() * 1000.0,
250 blocked_pct
251 );
252 }
253 println!();
254 }
255
256 #[must_use]
258 pub fn generate_summary(&self) -> String {
259 let mut summary = String::new();
260 let stats = self.profiler.calculate_stats();
261 let bottlenecks = self.profiler.identify_bottlenecks();
262
263 writeln!(summary, "Performance Summary:").unwrap();
264 writeln!(summary, " Tasks: {}", self.profiler.all_metrics().len()).unwrap();
265 writeln!(
266 summary,
267 " Mean duration: {:.2}ms",
268 stats.mean.as_secs_f64() * 1000.0
269 )
270 .unwrap();
271 writeln!(
272 summary,
273 " p95 duration: {:.2}ms",
274 stats.p95.as_secs_f64() * 1000.0
275 )
276 .unwrap();
277 writeln!(summary, " Bottlenecks: {}", bottlenecks.len()).unwrap();
278
279 summary
280 }
281
282 pub fn print_recommendations(&self) {
284 println!("┌────────────────────────────────────────────────────────────┐");
285 println!("│ Optimization Recommendations │");
286 println!("└────────────────────────────────────────────────────────────┘");
287
288 let bottlenecks = self.profiler.identify_bottlenecks();
289 let least_efficient = self.profiler.least_efficient_tasks(3);
290 let busiest = self.profiler.busiest_tasks(3);
291
292 let mut recommendations = Vec::new();
293
294 if !bottlenecks.is_empty() {
295 recommendations.push(format!(
296 "⚠️ {} bottleneck(s) detected - consider optimizing slow tasks",
297 bottlenecks.len()
298 ));
299 }
300
301 if !least_efficient.is_empty() {
302 let avg_efficiency: f64 = least_efficient.iter().map(|m| m.efficiency()).sum::<f64>()
303 / least_efficient.len() as f64;
304
305 if avg_efficiency < 0.5 {
306 recommendations.push(
307 "⚡ Low efficiency detected - tasks spending too much time blocked".to_string(),
308 );
309 recommendations.push(
310 " → Consider reducing await dependencies or using timeouts".to_string(),
311 );
312 }
313 }
314
315 if !busiest.is_empty() {
316 let max_polls = busiest[0].poll_count;
317 if max_polls > 100 {
318 recommendations.push(format!(
319 "🔄 Task with {max_polls} polls detected - possible busy loop or fine-grained awaits"
320 ));
321 recommendations.push(
322 " → Consider batching operations or using coarser-grained awaits".to_string(),
323 );
324 }
325 }
326
327 let hot_paths = self.profiler.get_hot_paths();
328 if let Some(hottest) = hot_paths.first() {
329 if hottest.execution_count > 100 {
330 recommendations.push(format!(
331 "🔥 Hot path detected: '{}' executed {} times",
332 hottest.path, hottest.execution_count
333 ));
334 recommendations
335 .push(" → Consider caching or memoization if appropriate".to_string());
336 }
337 }
338
339 if recommendations.is_empty() {
340 println!(" ✅ No major performance issues detected!");
341 println!(" ✨ Your async code looks well-optimized.");
342 } else {
343 for rec in recommendations {
344 println!(" {rec}");
345 }
346 }
347
348 println!();
349 }
350}