binius_m3/builder/
stat.rs1use std::fmt;
4
5use binius_field::TowerField;
6use binius_math::EvalCost;
7use binius_utils::{checked_arithmetics::log2_strict_usize, sparse_index::SparseIndex};
8
9use super::{Table, TablePartition};
10
11struct Constraint {
12 name: String,
13 degree: usize,
14 eval_cost: EvalCost,
15}
16
17#[derive(Default)]
18struct PerPartition {
19 constraints: Vec<Constraint>,
20}
21
22#[derive(Default)]
23struct PerTowerLevel {
24 per_v: SparseIndex<PerPartition>,
26}
27
28pub struct TableStat {
30 name: String,
31 per_tower_level: SparseIndex<PerTowerLevel>,
33 bits_per_row_committed: usize,
34 bits_per_row_virtual: usize,
35 total_flush_count: u64,
37}
38
39impl TableStat {
40 pub(super) fn new<F: TowerField>(table: &Table<F>) -> Self {
41 let mut bits_per_row_committed = 0;
42 let mut bits_per_row_virtual = 0;
43 for column in &table.columns {
44 let bits_per_column = 1 << column.shape.log_cell_size();
45 if matches!(column.col, super::ColumnDef::Committed { .. }) {
46 bits_per_row_committed += bits_per_column;
47 } else {
48 bits_per_row_virtual += bits_per_column;
49 }
50 }
51
52 let mut total_flush_count = 0u64;
54 for (_, partition) in table.partitions.iter() {
55 for flush in &partition.flushes {
56 total_flush_count += flush.multiplicity as u64;
57 }
58 }
59
60 let mut me = Self {
61 name: table.name.clone(),
62 per_tower_level: SparseIndex::new(),
63 bits_per_row_committed,
64 bits_per_row_virtual,
65 total_flush_count,
66 };
67
68 for (_, partition) in table.partitions.iter() {
69 let &TablePartition {
70 values_per_row,
71 zero_constraints,
72 ..
73 } = &partition;
74 let v_log2 = log2_strict_usize(*values_per_row);
75 for zero_constraint in zero_constraints {
76 let tower_level = zero_constraint.tower_level;
77 let name = zero_constraint.name.clone();
78 let degree = zero_constraint.expr.degree();
79 let eval_cost = zero_constraint.expr.eval_cost();
80 me.add_constraint(
81 tower_level,
82 v_log2,
83 Constraint {
84 name,
85 degree,
86 eval_cost,
87 },
88 );
89 }
90 }
91
92 me
93 }
94
95 fn add_constraint(&mut self, tower_level: usize, v_log2: usize, c: Constraint) {
96 let per_tower_level = self
97 .per_tower_level
98 .entry(tower_level)
99 .or_insert_with(PerTowerLevel::default);
100 let per_partition = per_tower_level
101 .per_v
102 .entry(v_log2)
103 .or_insert_with(PerPartition::default);
104 per_partition.constraints.push(c);
105 }
106
107 pub fn name(&self) -> &str {
109 &self.name
110 }
111
112 pub fn assert_zero_cost_approx(&self) -> usize {
127 let mut cost = 0;
128 for (tower_level, per_tower_level) in self.per_tower_level.iter() {
129 for (v_log2, per_partition) in per_tower_level.per_v.iter() {
130 for constraint in &per_partition.constraints {
131 let values_per_row = 1 << v_log2;
132 let bits = (1 << tower_level).max(8) * values_per_row;
137 let degree = constraint.degree;
138 let mult_cost_approx = constraint.eval_cost.mult_cost_approx();
139 cost += bits * degree * mult_cost_approx;
140 }
141 }
142 }
143 cost
144 }
145
146 pub fn bits_per_row_committed(&self) -> usize {
154 self.bits_per_row_committed
155 }
156
157 pub fn bits_per_row_virtual(&self) -> usize {
168 self.bits_per_row_virtual
169 }
170
171 pub fn total_flush_count(&self) -> u64 {
177 self.total_flush_count
178 }
179}
180
181impl fmt::Display for TableStat {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 writeln!(f, "table '{}':", self.name)?;
184 writeln!(f, "* bits per row: {}", self.bits_per_row_committed + self.bits_per_row_virtual)?;
185 writeln!(f, " committed: {}", self.bits_per_row_committed)?;
186 writeln!(f, " virtual: {}", self.bits_per_row_virtual)?;
187 writeln!(f, "* total flush count: {}", self.total_flush_count)?;
188 writeln!(f, "* zero checks:")?;
189 for (tower_level, per_tower_level) in self.per_tower_level.iter() {
190 let bits = 1 << tower_level;
191 writeln!(f, " B{bits}:")?;
192 for (v_log2, per_partition) in per_tower_level.per_v.iter() {
193 let values_per_row = 1 << v_log2;
194 writeln!(f, " values_per_row={values_per_row}:")?;
195 for (i, constraint) in per_partition.constraints.iter().enumerate() {
196 let ordinal = i + 1;
197 let Constraint {
198 name,
199 degree,
200 eval_cost,
201 } = constraint;
202 let n_adds = eval_cost.n_adds;
203 let n_muls = eval_cost.n_muls;
204 let n_squares = eval_cost.n_squares;
205 writeln!(
206 f,
207 " {ordinal}. {name}: deg={degree}, #+={n_adds}, #×={n_muls}, #²={n_squares}"
208 )?;
209 }
210 }
211 }
212
213 let cost = self.assert_zero_cost_approx();
214 writeln!(f, "Total approximate assert_zero costs: {cost}")?;
215 Ok(())
216 }
217}