1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
use crate::{
evm_circuit::{
step::ExecutionState, table::Table, util::constraint_builder::EVMConstraintBuilder,
},
util::cell_manager::CellType,
};
use eth_types::Field;
use itertools::Itertools;
type StepSize = Vec<(CellType, ColumnSize)>;
/// Contains (width, height, num_cells)
type ColumnSize = (usize, usize, usize);
/// Instrument captures metrics during the compilation of a circuit.
#[derive(Clone, Debug, Default)]
pub struct Instrument {
// States -> Cell Types -> (width, height, num_cells)
states: Vec<(ExecutionState, StepSize)>,
}
impl Instrument {
/// Collects `CellManager` stats from a compiled EVMCircuit in order to
/// extract metrics.
pub(crate) fn on_gadget_built<F: Field>(
&mut self,
execution_state: ExecutionState,
cb: &EVMConstraintBuilder<F>,
) {
let sizes = cb
.curr
.cell_manager
.get_stats()
.into_iter()
.sorted()
.collect::<Vec<_>>();
self.states.push((execution_state, sizes));
}
/// Disassembles the instrumentation data and returns a collection of
/// `ExecStateReport`s. One for each EVM `ExecutionState`.
pub fn analyze(&self) -> Vec<ExecStateReport> {
let mut report_collection = vec![];
for (state, sizes) in &self.states {
// Create a state report
let mut report = ExecStateReport::from(state);
// Compute max_height required for any kind of CellType for the current
// `ExecutionState`.
let top_height: usize = sizes.iter().map(|(_, (_, h, _))| *h).max().unwrap();
// Obtain `ExecutionState` metrics per column type.
for (cell_type, (width, _, cells)) in sizes {
let unused_cells = width * top_height - cells;
let total_available_cells = width * top_height;
let utilization =
((*cells as f64) / (*width as f64 * top_height as f64) * 100f64).round();
let data_entry = StateReportRow {
available_cells: total_available_cells,
unused_cells,
used_cells: *cells,
top_height,
used_columns: cells / top_height,
utilization,
};
match cell_type {
CellType::StoragePhase1 => {
report.storage_1 = data_entry;
}
CellType::StoragePhase2 => {
report.storage_2 = data_entry;
}
CellType::StoragePermutation => {
report.storage_perm = data_entry;
}
CellType::Lookup(Table::U8) => {
report.u8_lookup = data_entry;
}
CellType::Lookup(Table::U16) => {
report.u16_lookup = data_entry;
}
CellType::Lookup(Table::Fixed) => {
report.fixed_table = data_entry;
}
CellType::Lookup(Table::Tx) => {
report.tx_table = data_entry;
}
CellType::Lookup(Table::Rw) => {
report.rw_table = data_entry;
}
CellType::Lookup(Table::Bytecode) => {
report.bytecode_table = data_entry;
}
CellType::Lookup(Table::Block) => {
report.block_table = data_entry;
}
CellType::Lookup(Table::Copy) => {
report.copy_table = data_entry;
}
CellType::Lookup(Table::Keccak) => {
report.keccak_table = data_entry;
}
CellType::Lookup(Table::Exp) => {
report.exp_table = data_entry;
}
CellType::Lookup(Table::Sig) => {
report.sig_table = data_entry;
}
CellType::Lookup(Table::ChunkCtx) => {
report.chunk_ctx_table = data_entry;
}
}
}
report_collection.push(report);
}
report_collection
}
}
/// Struct which contains a Cost/ColumnType report for a particular EVM
/// [`ExecStep`](bus_mapping::circuit_input_builder::ExecStep).
#[derive(Clone, Debug, Default)]
pub struct ExecStateReport {
pub state: ExecutionState,
pub storage_1: StateReportRow,
pub storage_2: StateReportRow,
pub storage_perm: StateReportRow,
pub u8_lookup: StateReportRow,
pub u16_lookup: StateReportRow,
pub byte_lookup: StateReportRow,
pub fixed_table: StateReportRow,
pub tx_table: StateReportRow,
pub rw_table: StateReportRow,
pub bytecode_table: StateReportRow,
pub block_table: StateReportRow,
pub copy_table: StateReportRow,
pub keccak_table: StateReportRow,
pub exp_table: StateReportRow,
pub sig_table: StateReportRow,
pub chunk_ctx_table: StateReportRow,
}
impl From<ExecutionState> for ExecStateReport {
fn from(state: ExecutionState) -> Self {
ExecStateReport {
state,
..Default::default()
}
}
}
impl From<&ExecutionState> for ExecStateReport {
fn from(state: &ExecutionState) -> Self {
ExecStateReport {
state: *state,
..Default::default()
}
}
}
/// Struct that contains all of the measurament values required to evaluate the
/// costs of a particular `ColumnType` of an `ExecStateReport`
#[derive(Debug, Clone, Default)]
pub struct StateReportRow {
// Given a rigion of x columns and y rows, we have x * y cells available for computation.
pub available_cells: usize,
// The cells not used in the computation in the x*y region. These are the wasted cells.
pub unused_cells: usize,
// The cells used in the computation in the x*y region.
pub used_cells: usize,
// The largest y within all the `CellType`.
pub top_height: usize,
// If we fully utilize y, how large is the x really needed?
pub used_columns: usize,
// The percentage of cells used in computation in the x * y region.
pub utilization: f64,
}