1use std::{io::Write, path::PathBuf};
2
3use eyre::Result;
4use itertools::Itertools;
5
6use crate::{
7 aggregate::{
8 AggregateMetrics, EXECUTE_METERED_TIME_LABEL, EXECUTE_PREFLIGHT_INSNS_LABEL,
9 EXECUTE_PREFLIGHT_TIME_LABEL, MAIN_CELLS_USED_LABEL, PROOF_TIME_LABEL,
10 PROVE_EXCL_TRACE_TIME_LABEL, TRACE_GEN_TIME_LABEL,
11 },
12 types::MdTableCell,
13};
14
15#[derive(Clone, Debug)]
16pub struct GithubSummary {
17 pub rows: Vec<SummaryRow>,
18 pub benchmark_results_link: String,
19}
20
21#[derive(Clone, Debug)]
22pub struct SummaryRow {
23 pub name: String,
24 pub md_filename: String,
25 pub metrics: BenchSummaryMetrics,
26}
27
28#[derive(Clone, Debug)]
29pub struct BenchSummaryMetrics {
30 pub app: SingleSummaryMetrics,
31 pub leaf: Option<SingleSummaryMetrics>,
32 pub internals: Vec<SingleSummaryMetrics>,
33 pub root: Option<SingleSummaryMetrics>,
34 pub halo2_outer: Option<SingleSummaryMetrics>,
35 pub halo2_wrapper: Option<SingleSummaryMetrics>,
36}
37
38#[derive(Clone, Debug)]
39pub struct SingleSummaryMetrics {
40 pub proof_time_ms: MdTableCell,
41 pub par_proof_time_ms: MdTableCell,
43 pub cells_used: MdTableCell,
44 pub insns: MdTableCell,
45}
46
47impl GithubSummary {
48 pub fn new(
49 names: &[String],
50 aggregated_metrics: &[(AggregateMetrics, Option<AggregateMetrics>)],
51 md_paths: &[PathBuf],
52 benchmark_results_link: &str,
53 ) -> Self {
54 let rows = aggregated_metrics
55 .iter()
56 .zip_eq(md_paths.iter())
57 .zip_eq(names)
58 .map(|(((aggregated, prev_aggregated), md_path), name)| {
59 let md_filename = md_path
60 .file_name()
61 .expect("Path should have a filename")
62 .to_str()
63 .expect("Filename should be valid UTF-8");
64 let mut row = aggregated.get_summary_row(md_filename).unwrap_or_else(|| {
65 panic!("Failed to get summary row for file '{md_filename}'")
66 });
67 if let Some(prev_aggregated) = prev_aggregated {
68 if let Some(prev_row) = prev_aggregated.get_summary_row(md_filename) {
70 if row.name == prev_row.name {
71 row.metrics.set_diff(&prev_row.metrics);
72 }
73 }
74 }
75 row.name = name.clone();
76 row
77 })
78 .collect();
79
80 Self {
81 rows,
82 benchmark_results_link: benchmark_results_link.to_string(),
83 }
84 }
85
86 pub fn write_markdown(&self, writer: &mut impl Write) -> Result<()> {
87 writeln!(writer, "| group | app.proof_time_ms | app.cycles | app.cells_used | leaf.proof_time_ms | leaf.cycles | leaf.cells_used |")?;
88 write!(writer, "| -- |")?;
89 for _ in 0..6 {
90 write!(writer, " -- |")?;
91 }
92 writeln!(writer)?;
93
94 for row in self.rows.iter() {
95 write!(
96 writer,
97 "| [{}]({}/{}) |",
98 row.name, self.benchmark_results_link, row.md_filename
99 )?;
100 row.metrics.write_partial_md_row(writer)?;
101 writeln!(writer)?;
102 }
103 writeln!(writer)?;
104
105 Ok(())
106 }
107}
108
109impl BenchSummaryMetrics {
110 pub fn write_partial_md_row(&self, writer: &mut impl Write) -> Result<()> {
111 self.app.write_partial_md_row(writer)?;
112 if let Some(leaf) = &self.leaf {
113 leaf.write_partial_md_row(writer)?;
114 } else {
115 write!(writer, "- | - | - |")?;
117 }
118 Ok(())
128 }
129
130 pub fn set_diff(&mut self, prev: &Self) {
131 self.app.set_diff(&prev.app);
132 if let (Some(leaf), Some(prev_leaf)) = (&mut self.leaf, &prev.leaf) {
133 leaf.set_diff(prev_leaf);
134 }
135 for (internal, prev_internal) in self.internals.iter_mut().zip(prev.internals.iter()) {
136 internal.set_diff(prev_internal);
137 }
138 if let (Some(root), Some(prev_root)) = (&mut self.root, &prev.root) {
139 root.set_diff(prev_root);
140 }
141 }
142}
143
144impl SingleSummaryMetrics {
145 pub fn write_partial_md_row(&self, writer: &mut impl Write) -> Result<()> {
146 write!(
147 writer,
148 "{} | {} | {} |",
149 self.proof_time_ms, self.insns, self.cells_used,
150 )?;
151 Ok(())
152 }
153
154 pub fn set_diff(&mut self, prev: &Self) {
155 self.cells_used.diff = Some(self.cells_used.val - prev.cells_used.val);
156 self.insns.diff = Some(self.insns.val - prev.insns.val);
157 self.proof_time_ms.diff = Some(self.proof_time_ms.val - prev.proof_time_ms.val);
158 }
159}
160
161impl AggregateMetrics {
162 pub fn get_single_summary(&self, name: &str) -> Option<SingleSummaryMetrics> {
163 let stats = self.by_group.get(name)?;
164 let proof_time_ms = if let Some(proof_stats) = stats.get(PROOF_TIME_LABEL) {
166 proof_stats.sum
167 } else {
168 let execute_metered = stats
170 .get(EXECUTE_METERED_TIME_LABEL)
171 .map(|s| s.sum.val)
172 .unwrap_or(0.0);
173 let execute_preflight = stats
174 .get(EXECUTE_PREFLIGHT_TIME_LABEL)
175 .map(|s| s.sum.val)
176 .unwrap_or(0.0);
177 let trace_gen = stats
179 .get(TRACE_GEN_TIME_LABEL)
180 .map(|s| s.sum.val)
181 .unwrap_or(0.0);
182 let stark_prove = stats
183 .get(PROVE_EXCL_TRACE_TIME_LABEL)
184 .map(|s| s.sum.val)
185 .unwrap_or(0.0);
186 println!("{execute_metered} {execute_preflight} {trace_gen} {stark_prove}");
187 MdTableCell::new(
188 execute_metered + execute_preflight + trace_gen + stark_prove,
189 None,
190 )
191 };
192 println!("{}", self.total_proof_time.val);
193 let par_proof_time_ms = if let Some(proof_stats) = stats.get(PROOF_TIME_LABEL) {
194 proof_stats.max
195 } else {
196 let execute_metered = stats
198 .get(EXECUTE_METERED_TIME_LABEL)
199 .map(|s| s.max.val)
200 .unwrap_or(0.0);
201 let execute_preflight = stats
202 .get(EXECUTE_PREFLIGHT_TIME_LABEL)
203 .map(|s| s.max.val)
204 .unwrap_or(0.0);
205 let trace_gen = stats
206 .get(TRACE_GEN_TIME_LABEL)
207 .map(|s| s.max.val)
208 .unwrap_or(0.0);
209 let stark_prove = stats
210 .get(PROVE_EXCL_TRACE_TIME_LABEL)
211 .map(|s| s.max.val)
212 .unwrap_or(0.0);
213 MdTableCell::new(
214 execute_metered + execute_preflight + trace_gen + stark_prove,
215 None,
216 )
217 };
218 let cells_used = stats
219 .get(MAIN_CELLS_USED_LABEL)
220 .map(|s| s.sum)
221 .unwrap_or_default();
222 let insns = stats
223 .get(EXECUTE_PREFLIGHT_INSNS_LABEL)
224 .map(|s| s.sum)
225 .unwrap_or_default();
226 Some(SingleSummaryMetrics {
227 cells_used,
228 insns,
229 proof_time_ms,
230 par_proof_time_ms,
231 })
232 }
233
234 pub fn get_summary_row(&self, md_filename: &str) -> Option<SummaryRow> {
236 let app_name = self.name();
237 let app = self.get_single_summary(&app_name)?;
238 let leaf = self.get_single_summary("leaf");
239 let mut internals = Vec::new();
240 let mut hgt = 0;
241 while let Some(internal) = self.get_single_summary(&format!("internal.{hgt}")) {
242 internals.push(internal);
243 hgt += 1;
244 }
245 let root = self.get_single_summary("root");
246 let halo2_outer = self.get_single_summary("halo2_outer");
247 let halo2_wrapper = self.get_single_summary("halo2_wrapper");
248 Some(SummaryRow {
249 name: app_name.to_string(),
250 md_filename: md_filename.to_string(),
251 metrics: BenchSummaryMetrics {
252 app,
253 leaf,
254 internals,
255 root,
256 halo2_outer,
257 halo2_wrapper,
258 },
259 })
260 }
261}