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!(
187 "{} {} {} {}",
188 execute_metered, execute_preflight, trace_gen, stark_prove
189 );
190 MdTableCell::new(
191 execute_metered + execute_preflight + trace_gen + stark_prove,
192 None,
193 )
194 };
195 println!("{}", self.total_proof_time.val);
196 let par_proof_time_ms = if let Some(proof_stats) = stats.get(PROOF_TIME_LABEL) {
197 proof_stats.max
198 } else {
199 let execute_metered = stats
201 .get(EXECUTE_METERED_TIME_LABEL)
202 .map(|s| s.max.val)
203 .unwrap_or(0.0);
204 let execute_preflight = stats
205 .get(EXECUTE_PREFLIGHT_TIME_LABEL)
206 .map(|s| s.max.val)
207 .unwrap_or(0.0);
208 let trace_gen = stats
209 .get(TRACE_GEN_TIME_LABEL)
210 .map(|s| s.max.val)
211 .unwrap_or(0.0);
212 let stark_prove = stats
213 .get(PROVE_EXCL_TRACE_TIME_LABEL)
214 .map(|s| s.max.val)
215 .unwrap_or(0.0);
216 MdTableCell::new(
217 execute_metered + execute_preflight + trace_gen + stark_prove,
218 None,
219 )
220 };
221 let cells_used = stats
222 .get(MAIN_CELLS_USED_LABEL)
223 .map(|s| s.sum)
224 .unwrap_or_default();
225 let insns = stats
226 .get(EXECUTE_PREFLIGHT_INSNS_LABEL)
227 .map(|s| s.sum)
228 .unwrap_or_default();
229 Some(SingleSummaryMetrics {
230 cells_used,
231 insns,
232 proof_time_ms,
233 par_proof_time_ms,
234 })
235 }
236
237 pub fn get_summary_row(&self, md_filename: &str) -> Option<SummaryRow> {
239 let app_name = self.name();
240 let app = self.get_single_summary(&app_name)?;
241 let leaf = self.get_single_summary("leaf");
242 let mut internals = Vec::new();
243 let mut hgt = 0;
244 while let Some(internal) = self.get_single_summary(&format!("internal.{hgt}")) {
245 internals.push(internal);
246 hgt += 1;
247 }
248 let root = self.get_single_summary("root");
249 let halo2_outer = self.get_single_summary("halo2_outer");
250 let halo2_wrapper = self.get_single_summary("halo2_wrapper");
251 Some(SummaryRow {
252 name: app_name.to_string(),
253 md_filename: md_filename.to_string(),
254 metrics: BenchSummaryMetrics {
255 app,
256 leaf,
257 internals,
258 root,
259 halo2_outer,
260 halo2_wrapper,
261 },
262 })
263 }
264}