openvm_stark_backend/prover/
metrics.rs1use std::fmt::Display;
2
3use itertools::zip_eq;
4use serde::{Deserialize, Serialize};
5use tracing::{debug, info};
6
7use crate::{
8 keygen::types::TraceWidth,
9 proof::TraceVData,
10 prover::{DeviceMultiStarkProvingKey, ProverBackend},
11 StarkProtocolConfig,
12};
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
15pub struct TraceMetrics {
16 pub per_air: Vec<SingleTraceMetrics>,
17 pub total_cells: usize,
19 pub trace_height_inequalities: Vec<(usize, usize)>,
21}
22
23#[derive(Clone, Debug, Serialize, Deserialize)]
24pub struct SingleTraceMetrics {
25 pub air_name: String,
26 pub air_id: usize,
27 pub height: usize,
28 pub width: TraceWidth,
29 pub cells: TraceCells,
30 pub total_cells: usize,
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct TraceCells {
39 pub preprocessed: Option<usize>,
40 pub cached_mains: Vec<usize>,
41 pub common_main: usize,
42}
43
44impl Display for TraceMetrics {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 for (i, (weighted_sum, threshold)) in self.trace_height_inequalities.iter().enumerate() {
47 writeln!(
48 f,
49 "trace_height_constraint_{i} | weighted_sum = {:<10} | threshold = {:<10}",
50 format_number_with_underscores(*weighted_sum),
51 format_number_with_underscores(*threshold)
52 )?;
53 }
54 for trace_metrics in &self.per_air {
55 writeln!(f, "{}", trace_metrics)?;
56 }
57 Ok(())
58 }
59}
60
61impl Display for SingleTraceMetrics {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(
64 f,
65 "{:<20} | Rows = {:<10} | Cells = {:<11} | Prep Cols = {:<5} | Main Cols = {:<5}",
66 self.air_name,
67 format_number_with_underscores(self.height),
68 format_number_with_underscores(self.total_cells),
69 self.width.preprocessed.unwrap_or(0),
70 format!("{:?}", self.width.main_widths()),
71 )?;
72 Ok(())
73 }
74}
75
76pub fn trace_metrics<SC: StarkProtocolConfig, PB: ProverBackend>(
78 mpk: &DeviceMultiStarkProvingKey<PB>,
79 trace_vdata: &[Option<TraceVData<SC>>],
80) -> TraceMetrics {
81 let heights = trace_vdata
82 .iter()
83 .map(|vdata| vdata.as_ref().map(|v| 1 << v.log_height).unwrap_or(0))
84 .collect::<Vec<_>>();
85 let trace_height_inequalities = mpk
86 .trace_height_constraints
87 .iter()
88 .map(|trace_height_constraint| {
89 let weighted_sum = heights
90 .iter()
91 .enumerate()
92 .map(|(air_idx, h)| (trace_height_constraint.coefficients[air_idx] as usize) * h)
93 .sum::<usize>();
94 (weighted_sum, trace_height_constraint.threshold as usize)
95 })
96 .collect::<Vec<_>>();
97 let per_air: Vec<_> = zip_eq(&mpk.per_air, heights)
98 .enumerate()
99 .filter(|(_, (_, height))| *height > 0)
100 .map(|(air_idx, (pk, height))| {
101 let air_name = &pk.air_name;
102 let width = pk.vk.params.width.clone();
103 let cells = TraceCells {
104 preprocessed: width.preprocessed.map(|w| w * height),
105 cached_mains: width.cached_mains.iter().map(|w| w * height).collect(),
106 common_main: width.common_main * height,
107 };
108 let total_cells = cells
109 .cached_mains
110 .iter()
111 .chain([&cells.common_main])
112 .sum::<usize>();
113 SingleTraceMetrics {
114 air_name: air_name.to_string(),
115 air_id: air_idx,
116 height,
117 width,
118 cells,
119 total_cells,
120 }
121 })
122 .collect();
123 let total_cells = per_air.iter().map(|m| m.total_cells).sum();
124 let metrics = TraceMetrics {
125 per_air,
126 total_cells,
127 trace_height_inequalities,
128 };
129 info!(
130 "total_trace_cells = {} (excluding preprocessed)",
131 format_number_with_underscores(metrics.total_cells)
132 );
133 info!(
134 "preprocessed_trace_cells = {}",
135 format_number_with_underscores(
136 metrics
137 .per_air
138 .iter()
139 .map(|m| m.cells.preprocessed.unwrap_or(0))
140 .sum::<usize>()
141 )
142 );
143 info!(
144 "main_trace_cells = {}",
145 format_number_with_underscores(
146 metrics
147 .per_air
148 .iter()
149 .map(|m| m.cells.cached_mains.iter().sum::<usize>() + m.cells.common_main)
150 .sum::<usize>()
151 )
152 );
153 debug!("{}", metrics);
154 metrics
155}
156
157pub fn format_number_with_underscores(n: usize) -> String {
158 let num_str = n.to_string();
159 let mut result = String::new();
160
161 for (i, c) in num_str.chars().rev().enumerate() {
163 if i > 0 && i % 3 == 0 {
164 result.push('_');
165 }
166 result.push(c);
167 }
168
169 result.chars().rev().collect()
171}
172
173#[cfg(feature = "metrics")]
174mod emit {
175 use metrics::counter;
176
177 use super::{SingleTraceMetrics, TraceMetrics};
178
179 impl TraceMetrics {
180 pub fn emit(&self) {
181 for (i, (weighted_sum, threshold)) in self.trace_height_inequalities.iter().enumerate()
182 {
183 let labels = [("trace_height_constraint", i.to_string())];
184 counter!("weighted_sum", &labels).absolute(*weighted_sum as u64);
185 counter!("threshold", &labels).absolute(*threshold as u64);
186 }
187 for trace_metrics in &self.per_air {
188 trace_metrics.emit();
189 }
190 counter!("total_cells").absolute(self.total_cells as u64);
191 }
192 }
193
194 impl SingleTraceMetrics {
195 pub fn emit(&self) {
196 let labels = [
197 ("air_name", self.air_name.clone()),
198 ("air_id", self.air_id.to_string()),
199 ];
200 counter!("rows", &labels).absolute(self.height as u64);
201 counter!("cells", &labels).absolute(self.total_cells as u64);
202 counter!("prep_cols", &labels).absolute(self.width.preprocessed.unwrap_or(0) as u64);
203 counter!("main_cols", &labels).absolute(
204 (self.width.cached_mains.iter().sum::<usize>() + self.width.common_main) as u64,
205 );
206 }
207 }
208}