1use std::{cmp::max, mem::size_of};
4
5use crate::{StarkProtocolConfig, SystemParams};
6
7pub const INTERACTION_MEMORY_OVERHEAD: usize = 2 << 20;
9
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub struct ProvingMemoryCounts {
17 pub main_cells_with_rot: usize,
19 pub main_cells_without_rot: usize,
21 pub interaction_cells: usize,
23}
24
25impl ProvingMemoryCounts {
26 pub const fn new(
27 main_cells_with_rot: usize,
28 main_cells_without_rot: usize,
29 interaction_cells: usize,
30 ) -> Self {
31 Self {
32 main_cells_with_rot,
33 main_cells_without_rot,
34 interaction_cells,
35 }
36 }
37
38 #[inline]
39 pub const fn main_cells(&self) -> usize {
40 self.main_cells_with_rot + self.main_cells_without_rot
41 }
42}
43
44#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
46pub struct ProvingMemoryEstimate {
47 pub total: usize,
49 pub main: usize,
51 pub rs_code_matrix: usize,
53 pub main_secondary: usize,
55 pub interaction: usize,
57 pub secondary_peak: usize,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq)]
63pub struct ProvingMemoryConfig {
64 pub base_field_size: usize,
66 pub extension_degree: usize,
68 pub log_blowup: usize,
70 pub l_skip: usize,
72 pub max_constraint_degree: usize,
74 pub cache_rs_code_matrix: bool,
76}
77
78impl ProvingMemoryConfig {
79 pub fn from_protocol_config<SC: StarkProtocolConfig>(
80 config: &SC,
81 cache_rs_code_matrix: bool,
82 ) -> Self {
83 Self::from_params::<SC::F>(config.params(), SC::D_EF, cache_rs_code_matrix)
84 }
85
86 fn from_params<F>(
87 params: &SystemParams,
88 extension_degree: usize,
89 cache_rs_code_matrix: bool,
90 ) -> Self {
91 Self {
92 base_field_size: size_of::<F>(),
93 extension_degree,
94 log_blowup: params.log_blowup,
95 l_skip: params.l_skip,
96 max_constraint_degree: params.max_constraint_degree,
97 cache_rs_code_matrix,
98 }
99 }
100
101 #[inline]
102 pub fn main_memory_bytes(&self, main_cells: usize) -> usize {
103 main_cells * self.base_field_size
104 }
105
106 #[inline]
107 pub fn rs_code_matrix_memory_bytes(&self, main_cells: usize) -> usize {
108 main_cells * (1usize << self.log_blowup) * self.base_field_size
109 }
110
111 #[inline]
112 pub fn main_cell_secondary_weight(&self) -> f64 {
113 default_main_cell_secondary_weight(
114 self.extension_degree,
115 self.l_skip,
116 self.max_constraint_degree,
117 )
118 }
119
120 #[inline]
133 pub fn main_secondary_memory_bytes_for_rot(&self, main_cells: usize, need_rot: bool) -> usize {
134 let main_cell_secondary_weight = self.main_cell_secondary_weight();
135 let weight = if need_rot {
136 2.0 * main_cell_secondary_weight
137 } else {
138 main_cell_secondary_weight
139 };
140 ceil_weighted_bytes(main_cells, self.base_field_size, weight)
141 }
142
143 #[inline]
144 pub fn main_secondary_memory_bytes(&self, counts: ProvingMemoryCounts) -> usize {
145 self.main_secondary_memory_bytes_for_rot(counts.main_cells_with_rot, true)
146 + self.main_secondary_memory_bytes_for_rot(counts.main_cells_without_rot, false)
147 }
148
149 #[inline]
150 pub fn interaction_memory_bytes_without_overhead(&self, interaction_cells: usize) -> usize {
151 ceil_weighted_bytes(
152 interaction_cells,
153 self.base_field_size,
154 default_interaction_cell_weight(self.extension_degree),
155 )
156 }
157
158 #[inline]
159 pub fn interaction_memory_bytes(&self, interaction_cells: usize) -> usize {
160 self.interaction_memory_bytes_without_overhead(interaction_cells)
161 + INTERACTION_MEMORY_OVERHEAD
162 }
163
164 #[inline]
189 pub fn estimate(&self, counts: ProvingMemoryCounts) -> ProvingMemoryEstimate {
190 let main_cells = counts.main_cells();
191 let main = self.main_memory_bytes(main_cells);
192 let rs_code_matrix = self.rs_code_matrix_memory_bytes(main_cells);
193 let main_secondary = self.main_secondary_memory_bytes(counts);
194 let interaction = self.interaction_memory_bytes(counts.interaction_cells);
195 let secondary_peak = if self.cache_rs_code_matrix {
196 rs_code_matrix + max(main_secondary, interaction)
197 } else {
198 max(rs_code_matrix, max(main_secondary, interaction))
199 };
200
201 ProvingMemoryEstimate {
202 total: main + secondary_peak,
203 main,
204 rs_code_matrix,
205 main_secondary,
206 interaction,
207 secondary_peak,
208 }
209 }
210}
211
212fn default_main_cell_secondary_weight(
213 extension_degree: usize,
214 l_skip: usize,
215 max_constraint_degree: usize,
216) -> f64 {
217 (1.0 + max_constraint_degree as f64 / 2.0) * extension_degree as f64 / (1usize << l_skip) as f64
218}
219
220fn default_interaction_cell_weight(extension_degree: usize) -> f64 {
230 (2 * extension_degree) as f64 * (1.0 + 2.0 * (1.0 / 16.0 + 1.0 / 256.0))
231}
232
233fn ceil_weighted_bytes(cell_count: usize, base_field_size: usize, weight: f64) -> usize {
235 ((cell_count * base_field_size) as f64 * weight).ceil() as usize
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::test_utils::default_test_params_small;
242
243 fn test_memory_config() -> ProvingMemoryConfig {
244 let params = default_test_params_small();
245 ProvingMemoryConfig::from_params::<u32>(¶ms, 4, true)
246 }
247
248 #[test]
249 fn dropped_rs_code_matrix_is_phase_disjoint() {
250 let params = default_test_params_small();
251 let config = ProvingMemoryConfig::from_params::<u32>(¶ms, 4, false);
252 let counts = ProvingMemoryCounts::new(10, 20, 5);
253
254 let estimate = config.estimate(counts);
255
256 assert_eq!(estimate.main, 30 * 4);
257 assert_eq!(estimate.rs_code_matrix, 30 * 2 * 4);
258 assert_eq!(estimate.total, estimate.main + estimate.secondary_peak);
259 assert_eq!(
260 estimate.secondary_peak,
261 max(
262 estimate.rs_code_matrix,
263 max(estimate.main_secondary, estimate.interaction)
264 )
265 );
266 }
267
268 #[test]
269 fn cached_rs_code_matrix_is_additive() {
270 let config = test_memory_config();
271 let counts = ProvingMemoryCounts::new(10, 20, 5);
272
273 let estimate = config.estimate(counts);
274
275 assert_eq!(
276 estimate.secondary_peak,
277 estimate.rs_code_matrix + max(estimate.main_secondary, estimate.interaction)
278 );
279 }
280}