1use super::BrickProfiler;
8use crate::brick::exec_graph::{BrickCategory, BrickId};
9
10impl BrickProfiler {
11 pub fn print_category_stats(&self) {
13 let cats = self.category_stats();
14 let total = self.total_ns;
15
16 println!("╭─────────────────────────────────────────────────────────╮");
17 println!("│ Category Breakdown (PAR-200) │");
18 println!("├─────────────────────────────────────────────────────────┤");
19 for (i, cat_stats) in cats.iter().enumerate() {
20 let cat = BrickCategory::ALL[i];
21 if cat_stats.count > 0 {
22 println!(
23 "│ {:12} {:8.2}µs avg {:6.1}% [{:5} samples] │",
24 cat.name(),
25 cat_stats.avg_us(),
26 cat_stats.percentage(total),
27 cat_stats.count
28 );
29 }
30 }
31 println!("╰─────────────────────────────────────────────────────────╯");
32 }
33
34 #[must_use]
36 pub fn summary(&self) -> String {
37 let mut report = String::new();
38 report.push_str("=== Brick Profiler Summary (PAR-200) ===\n");
39 report.push_str(&format!(
40 "Total: {} tokens, {:.2}µs, {:.1} tok/s\n",
41 self.total_tokens,
42 self.total_ns as f64 / 1000.0,
43 self.total_throughput()
44 ));
45 report.push_str("\nPer-Brick Breakdown:\n");
46
47 let mut all_stats: Vec<(&str, &crate::brick::exec_graph::BrickStats)> = Vec::new();
49
50 for (i, stats) in self.brick_stats.iter().enumerate() {
52 if stats.count > 0 {
53 let brick_id = BrickId::ALL[i];
54 all_stats.push((brick_id.name(), stats));
55 }
56 }
57
58 for (name, stats) in &self.dynamic_stats {
60 all_stats.push((name.as_str(), stats));
61 }
62
63 all_stats.sort_by(|a, b| b.1.total_ns.cmp(&a.1.total_ns));
65
66 for (name, stats) in &all_stats {
67 let pct = if self.total_ns > 0 {
68 100.0 * stats.total_ns as f64 / self.total_ns as f64
69 } else {
70 0.0
71 };
72 report.push_str(&format!(
73 " {:20} {:8.2}µs avg ({:5.1}%) [{} samples]\n",
74 name,
75 stats.avg_us(),
76 pct,
77 stats.count
78 ));
79 }
80
81 report.push_str("\nCategory Breakdown:\n");
83 let cats = self.category_stats();
84 for (i, cat_stats) in cats.iter().enumerate() {
85 if cat_stats.count > 0 {
86 let cat = BrickCategory::ALL[i];
87 report.push_str(&format!(
88 " {:12} {:8.2}µs avg ({:5.1}%)\n",
89 cat.name(),
90 cat_stats.avg_us(),
91 cat_stats.percentage(self.total_ns)
92 ));
93 }
94 }
95
96 report
97 }
98
99 #[must_use]
122 pub fn to_json(&self) -> String {
123 let mut bricks = Vec::new();
124
125 let mut all_stats: Vec<(&str, &crate::brick::exec_graph::BrickStats)> = Vec::new();
127
128 for (i, stats) in self.brick_stats.iter().enumerate() {
130 if stats.count > 0 {
131 let brick_id = BrickId::ALL[i];
132 all_stats.push((brick_id.name(), stats));
133 }
134 }
135
136 for (name, stats) in &self.dynamic_stats {
138 all_stats.push((name.as_str(), stats));
139 }
140
141 all_stats.sort_by(|a, b| b.1.total_ns.cmp(&a.1.total_ns));
143
144 for (name, stats) in all_stats {
145 let pct = if self.total_ns > 0 {
146 100.0 * stats.total_ns as f64 / self.total_ns as f64
147 } else {
148 0.0
149 };
150 let compression = stats.compression_ratio();
152 let throughput_gbps = stats.throughput_gbps();
153 let bottleneck = stats.get_bottleneck();
154 bricks.push(format!(
155 r#"{{"name":"{}","count":{},"total_ns":{},"avg_us":{:.2},"min_us":{:.2},"max_us":{:.2},"throughput":{:.1},"pct":{:.1},"total_bytes":{},"compression_ratio":{:.2},"throughput_gbps":{:.2},"bottleneck":"{}"}}"#,
156 name,
157 stats.count,
158 stats.total_ns,
159 stats.avg_us(),
160 stats.min_us(),
161 stats.max_us(),
162 stats.throughput(),
163 pct,
164 stats.total_bytes,
165 compression,
166 throughput_gbps,
167 bottleneck
168 ));
169 }
170
171 format!(
172 r#"{{"total_tokens":{},"total_ns":{},"total_throughput":{:.1},"bricks":[{}]}}"#,
173 self.total_tokens,
174 self.total_ns,
175 self.total_throughput(),
176 bricks.join(",")
177 )
178 }
179
180 pub fn write_json(&self, path: &std::path::Path) -> std::io::Result<()> {
185 std::fs::write(path, self.to_json())
186 }
187
188 #[must_use]
199 pub fn tile_summary(&self) -> String {
200 let mut report = String::new();
201 report.push_str("=== Tile Profiling Summary (TILING-SPEC-001) ===\n");
202 report.push_str("Level Samples Avg µs GFLOP/s AI Elements\n");
203
204 for stats in &self.tile_stats {
205 if stats.count > 0 {
206 report.push_str(&format!(
207 "{:8} {:9} {:8.1} {:8.2} {:4.2} {:10}\n",
208 stats.level.name(),
209 stats.count,
210 stats.avg_us(),
211 stats.gflops(),
212 stats.arithmetic_intensity(),
213 stats.total_elements / stats.count.max(1)
214 ));
215 }
216 }
217
218 report
219 }
220
221 #[must_use]
225 pub fn tile_stats_to_json(&self) -> String {
226 let tiles: Vec<String> = self
227 .tile_stats
228 .iter()
229 .filter(|s| s.count > 0)
230 .map(|s| {
231 format!(
232 r#"{{"level":"{}","count":{},"total_ns":{},"avg_us":{:.2},"min_us":{:.2},"max_us":{:.2},"gflops":{:.2},"arithmetic_intensity":{:.2},"total_elements":{},"total_flops":{}}}"#,
233 s.level.name(),
234 s.count,
235 s.total_ns,
236 s.avg_us(),
237 s.min_ns as f64 / 1000.0,
238 s.max_ns as f64 / 1000.0,
239 s.gflops(),
240 s.arithmetic_intensity(),
241 s.total_elements,
242 s.total_flops
243 )
244 })
245 .collect();
246
247 format!(
248 r#"{{"tile_profiling_enabled":{},"tiles":[{}]}}"#,
249 self.tile_profiling_enabled,
250 tiles.join(",")
251 )
252 }
253}