1use bytesize::ByteSize;
2use itertools::izip;
3use openvm_stark_backend::memory_metering::{ProvingMemoryConfig, ProvingMemoryCounts};
4use serde::{Deserialize, Serialize};
5
6use crate::utils::{add_one_or_zero, next_power_of_two_or_zero};
7
8pub const DEFAULT_SEGMENT_CHECK_INSNS: u64 = 1000;
9
10pub const DEFAULT_MAX_MEMORY: usize = 15 << 30; #[derive(derive_new::new, Clone, Debug, Serialize, Deserialize)]
13pub struct Segment {
14 pub instret_start: u64,
15 pub num_insns: u64,
16 pub trace_heights: Vec<u32>,
17}
18
19#[derive(Clone, Copy, Debug)]
20pub struct SegmentationLimits {
21 pub max_trace_height_bits: u8,
22 pub max_memory: usize,
23 pub max_interactions: u32,
24}
25
26#[derive(Clone, Debug)]
27struct SegmentationParams {
28 air_names: Vec<String>,
29 widths: Vec<usize>,
30 interactions: Vec<usize>,
31 need_rot: Vec<bool>,
32 constraint_eval_buffers: Vec<usize>,
33 max_trace_height: u32,
34 max_memory: usize,
35 max_interactions: u32,
36 memory_config: ProvingMemoryConfig,
37 segment_check_insns: u64,
38}
39
40impl SegmentationParams {
41 fn new(
42 air_names: Vec<String>,
43 widths: Vec<usize>,
44 interactions: Vec<usize>,
45 need_rot: Vec<bool>,
46 constraint_eval_buffers: Vec<usize>,
47 limits: SegmentationLimits,
48 memory_config: ProvingMemoryConfig,
49 ) -> Self {
50 assert_eq!(air_names.len(), widths.len());
51 assert_eq!(air_names.len(), interactions.len());
52 assert_eq!(air_names.len(), need_rot.len());
53 assert_eq!(air_names.len(), constraint_eval_buffers.len());
54 assert!(
55 limits.max_trace_height_bits < u32::BITS as u8,
56 "max_trace_height_bits must be less than {}",
57 u32::BITS
58 );
59
60 let max_trace_height = 1u32
61 .checked_shl(u32::from(limits.max_trace_height_bits))
62 .expect("max_trace_height_bits must fit in u32 trace height");
63 assert!(
64 u64::from(max_trace_height) >= 2 * DEFAULT_SEGMENT_CHECK_INSNS,
65 "max_trace_height must be at least twice DEFAULT_SEGMENT_CHECK_INSNS"
66 );
67
68 Self {
69 air_names,
70 widths,
71 interactions,
72 need_rot,
73 constraint_eval_buffers,
74 max_trace_height,
75 max_memory: limits.max_memory,
76 max_interactions: limits.max_interactions,
77 memory_config,
78 segment_check_insns: DEFAULT_SEGMENT_CHECK_INSNS,
79 }
80 }
81}
82
83#[derive(Clone, Debug)]
84pub struct SegmentationCtx {
85 pub segments: Vec<Segment>,
86 params: SegmentationParams,
87 pub instret: u64,
88 pub instrets_until_check: u64,
89 pub(crate) checkpoint_trace_heights: Vec<u32>,
91 checkpoint_instret: u64,
93}
94
95#[derive(Clone, Copy, Debug)]
96enum SegmentationTrigger {
97 Height {
98 #[cfg(feature = "metrics")]
99 air_id: usize,
100 },
101 Memory,
102 Interactions,
103}
104
105#[cfg(feature = "metrics")]
106impl SegmentationTrigger {
107 fn reason(self) -> &'static str {
108 match self {
109 SegmentationTrigger::Height { .. } => "height",
110 SegmentationTrigger::Memory => "memory",
111 SegmentationTrigger::Interactions => "interactions",
112 }
113 }
114}
115
116#[derive(Default)]
117struct MeteredCounts {
118 unpadded_rows: usize,
120 padding_rows: usize,
122 main_unpadded_with_rot: usize,
124 main_padding_with_rot: usize,
126 main_unpadded_no_rot: usize,
128 main_padding_no_rot: usize,
130 interaction_cells_unpadded: usize,
132 interaction_cells_padding: usize,
134 constraint_eval_buffers_unpadded: usize,
136 constraint_eval_buffers_padding: usize,
138}
139
140struct MeteredMemoryBreakdown {
141 total: usize,
143 unpadded: usize,
145}
146
147impl SegmentationCtx {
148 pub fn new(
149 air_names: Vec<String>,
150 widths: Vec<usize>,
151 interactions: Vec<usize>,
152 need_rot: Vec<bool>,
153 constraint_eval_buffers: Vec<usize>,
154 limits: SegmentationLimits,
155 memory_config: ProvingMemoryConfig,
156 ) -> Self {
157 let num_airs = air_names.len();
158 let params = SegmentationParams::new(
159 air_names,
160 widths,
161 interactions,
162 need_rot,
163 constraint_eval_buffers,
164 limits,
165 memory_config,
166 );
167 Self {
168 segments: Vec::new(),
169 instrets_until_check: params.segment_check_insns,
170 params,
171 instret: 0,
172 checkpoint_trace_heights: vec![0; num_airs],
173 checkpoint_instret: 0,
174 }
175 }
176
177 #[inline(always)]
178 pub(crate) fn air_names(&self) -> &[String] {
179 &self.params.air_names
180 }
181
182 #[inline(always)]
183 pub(crate) fn widths(&self) -> &[usize] {
184 &self.params.widths
185 }
186
187 #[inline(always)]
188 pub(super) fn segment_check_insns(&self) -> u64 {
189 self.params.segment_check_insns
190 }
191
192 pub fn set_max_memory(&mut self, max_memory: usize) {
193 self.params.max_memory = max_memory;
194 }
195
196 #[inline(always)]
198 fn calculate_max_trace_height_with_name(&self, trace_heights: &[u32]) -> (u32, &str) {
199 trace_heights
200 .iter()
201 .enumerate()
202 .map(|(i, &height)| (next_power_of_two_or_zero(height as usize) as u32, i))
203 .max_by_key(|(height, _)| *height)
204 .map(|(height, idx)| (height, self.params.air_names[idx].as_str()))
205 .unwrap_or((0, "unknown"))
206 }
207
208 #[inline(always)]
210 fn counts_to_memory(
211 &self,
212 main_cnt_with_rot: usize,
213 main_cnt_no_rot: usize,
214 interaction_cells: usize,
215 constraint_eval_cells: usize,
216 ) -> (
217 usize, usize, usize, ) {
221 let estimate = self.params.memory_config.estimate(ProvingMemoryCounts::new(
222 main_cnt_with_rot,
223 main_cnt_no_rot,
224 interaction_cells,
225 constraint_eval_cells,
226 ));
227 (estimate.total, estimate.main, estimate.secondary_peak)
228 }
229
230 #[inline(always)]
233 fn calculate_count_breakdown(&self, trace_heights: &[u32]) -> MeteredCounts {
234 debug_assert_eq!(trace_heights.len(), self.params.widths.len());
235 debug_assert_eq!(trace_heights.len(), self.params.interactions.len());
236 debug_assert_eq!(trace_heights.len(), self.params.need_rot.len());
237 debug_assert_eq!(
238 trace_heights.len(),
239 self.params.constraint_eval_buffers.len()
240 );
241
242 let mut counts = MeteredCounts::default();
243 for (&height, &width, &interactions, &need_rot, &constraint_eval_buffer) in izip!(
244 trace_heights,
245 &self.params.widths,
246 &self.params.interactions,
247 &self.params.need_rot,
248 &self.params.constraint_eval_buffers
249 ) {
250 let padded_height = next_power_of_two_or_zero(height as usize);
251 let unpadded_height = height as usize;
252 let padding_height = padded_height - unpadded_height;
253 counts.unpadded_rows += unpadded_height;
254 counts.padding_rows += padding_height;
255 let main_unpadded_cells = unpadded_height * width;
256 let main_padding_cells = padding_height * width;
257 if need_rot {
258 counts.main_unpadded_with_rot += main_unpadded_cells;
259 counts.main_padding_with_rot += main_padding_cells;
260 } else {
261 counts.main_unpadded_no_rot += main_unpadded_cells;
262 counts.main_padding_no_rot += main_padding_cells;
263 }
264 counts.interaction_cells_unpadded += unpadded_height * interactions;
265 counts.interaction_cells_padding += padding_height * interactions;
266 counts.constraint_eval_buffers_unpadded += unpadded_height * constraint_eval_buffer;
267 counts.constraint_eval_buffers_padding += padding_height * constraint_eval_buffer;
268 }
269 counts
270 }
271
272 #[inline(always)]
275 fn calculate_cell_counts(&self, trace_heights: &[u32]) -> (usize, usize, usize, usize) {
276 debug_assert_eq!(trace_heights.len(), self.params.widths.len());
277 debug_assert_eq!(trace_heights.len(), self.params.interactions.len());
278 debug_assert_eq!(trace_heights.len(), self.params.need_rot.len());
279 debug_assert_eq!(
280 trace_heights.len(),
281 self.params.constraint_eval_buffers.len()
282 );
283
284 let mut main_cnt_with_rot = 0;
285 let mut main_cnt_no_rot = 0;
286 let mut interaction_cells = 0;
287 let mut constraint_eval_cells = 0;
288 for (&height, &width, &interactions, &need_rot, &constraint_eval_buffer) in izip!(
289 trace_heights,
290 &self.params.widths,
291 &self.params.interactions,
292 &self.params.need_rot,
293 &self.params.constraint_eval_buffers
294 ) {
295 let padded_height = next_power_of_two_or_zero(height as usize);
296 let main_cells = padded_height * width;
297 if need_rot {
298 main_cnt_with_rot += main_cells;
299 } else {
300 main_cnt_no_rot += main_cells;
301 }
302 interaction_cells += padded_height * interactions;
303 constraint_eval_cells += padded_height * constraint_eval_buffer;
304 }
305 (
306 main_cnt_with_rot,
307 main_cnt_no_rot,
308 interaction_cells,
309 constraint_eval_cells,
310 )
311 }
312
313 #[inline(always)]
315 fn calculate_total_memory(
316 &self,
317 trace_heights: &[u32],
318 ) -> (
319 usize, usize, usize, ) {
323 let (main_cnt_with_rot, main_cnt_no_rot, interaction_cells, constraint_eval_cells) =
324 self.calculate_cell_counts(trace_heights);
325 self.counts_to_memory(
326 main_cnt_with_rot,
327 main_cnt_no_rot,
328 interaction_cells,
329 constraint_eval_cells,
330 )
331 }
332
333 #[inline(always)]
334 fn calculate_memory_breakdown(&self, counts: &MeteredCounts) -> MeteredMemoryBreakdown {
335 let unpadded = self.params.memory_config.estimate(ProvingMemoryCounts::new(
336 counts.main_unpadded_with_rot,
337 counts.main_unpadded_no_rot,
338 counts.interaction_cells_unpadded,
339 counts.constraint_eval_buffers_unpadded,
340 ));
341 let total = self.params.memory_config.estimate(ProvingMemoryCounts::new(
342 counts.main_unpadded_with_rot + counts.main_padding_with_rot,
343 counts.main_unpadded_no_rot + counts.main_padding_no_rot,
344 counts.interaction_cells_unpadded + counts.interaction_cells_padding,
345 counts.constraint_eval_buffers_unpadded + counts.constraint_eval_buffers_padding,
346 ));
347
348 MeteredMemoryBreakdown {
349 total: total.total,
350 unpadded: unpadded.total,
351 }
352 }
353
354 #[inline(always)]
358 fn calculate_total_interactions(&self, trace_heights: &[u32]) -> u64 {
359 debug_assert_eq!(trace_heights.len(), self.params.interactions.len());
360
361 trace_heights
362 .iter()
363 .zip(self.params.interactions.iter())
364 .map(|(&height, &interactions)| add_one_or_zero(height) as u64 * interactions as u64)
365 .sum()
366 }
367
368 #[inline(always)]
369 pub(crate) fn should_segment(
370 &self,
371 instret: u64,
372 trace_heights: &[u32],
373 is_trace_height_constant: &[bool],
374 ) -> bool {
375 self.segmentation_trigger(instret, trace_heights, is_trace_height_constant)
376 .is_some()
377 }
378
379 #[inline(always)]
380 fn segmentation_trigger(
381 &self,
382 instret: u64,
383 trace_heights: &[u32],
384 is_trace_height_constant: &[bool],
385 ) -> Option<SegmentationTrigger> {
386 debug_assert_eq!(trace_heights.len(), is_trace_height_constant.len());
387 debug_assert_eq!(trace_heights.len(), self.params.air_names.len());
388 debug_assert_eq!(trace_heights.len(), self.params.widths.len());
389 debug_assert_eq!(trace_heights.len(), self.params.interactions.len());
390 debug_assert_eq!(trace_heights.len(), self.params.need_rot.len());
391
392 let instret_start = self
393 .segments
394 .last()
395 .map_or(0, |s| s.instret_start + s.num_insns);
396 let num_insns = instret - instret_start;
397
398 if num_insns == 0 {
400 return None;
401 }
402
403 let mut main_cnt_with_rot = 0usize;
404 let mut main_cnt_no_rot = 0usize;
405 let mut interaction_cells = 0usize;
406 let mut constraint_eval_cells = 0usize;
407 let padded_heights = trace_heights
408 .iter()
409 .map(|&height| next_power_of_two_or_zero(height as usize) as u32);
410 for (i, row) in izip!(
411 padded_heights,
412 &self.params.widths,
413 &self.params.interactions,
414 is_trace_height_constant,
415 &self.params.need_rot,
416 &self.params.constraint_eval_buffers
417 )
418 .enumerate()
419 {
420 let (padded_height, &width, &interactions, &is_constant, &need_rot, &constraint_eval) =
421 row;
422 if !is_constant && padded_height > self.params.max_trace_height {
425 let air_name = unsafe { self.params.air_names.get_unchecked(i) };
426 tracing::info!(
427 "overshoot: instret {:10} | height ({:8}) > max ({:8}) | chip {:3} ({}) ",
428 instret,
429 padded_height,
430 self.params.max_trace_height,
431 i,
432 air_name,
433 );
434 return Some(SegmentationTrigger::Height {
435 #[cfg(feature = "metrics")]
436 air_id: i,
437 });
438 }
439 let main_cells = padded_height as usize * width;
440 if need_rot {
441 main_cnt_with_rot += main_cells;
442 } else {
443 main_cnt_no_rot += main_cells;
444 }
445 interaction_cells += padded_height as usize * interactions;
446 constraint_eval_cells += padded_height as usize * constraint_eval;
447 }
448
449 let (total_memory, main_memory, interaction_memory) = self.counts_to_memory(
450 main_cnt_with_rot,
451 main_cnt_no_rot,
452 interaction_cells,
453 constraint_eval_cells,
454 );
455 if total_memory > self.params.max_memory {
456 tracing::info!(
457 "overshoot: instret {:10} | total memory ({:5}) > max ({:5}) | main ({:5}) | interaction ({:5})",
458 instret,
459 ByteSize::b(total_memory as u64),
460 ByteSize::b(self.params.max_memory as u64),
461 ByteSize::b(main_memory as u64),
462 ByteSize::b(interaction_memory as u64),
463 );
464 return Some(SegmentationTrigger::Memory);
465 }
466
467 let total_interactions = self.calculate_total_interactions(trace_heights);
468 if total_interactions > u64::from(self.params.max_interactions) {
469 tracing::info!(
470 "overshoot: instret {:10} | total interactions ({:10}) > max ({:10})",
471 instret,
472 total_interactions,
473 self.params.max_interactions
474 );
475 return Some(SegmentationTrigger::Interactions);
476 }
477
478 None
479 }
480
481 #[inline(always)]
482 pub fn check_and_segment(
483 &mut self,
484 instret: u64,
485 trace_heights: &mut [u32],
486 is_trace_height_constant: &[bool],
487 ) -> bool {
488 let trigger = self.segmentation_trigger(instret, trace_heights, is_trace_height_constant);
489 let should_segment = trigger.is_some();
490
491 #[cfg(feature = "metrics")]
492 if let Some(trigger) = trigger {
493 self.emit_segmentation_trigger_metric(trigger);
494 }
495
496 if should_segment {
497 self.create_segment_from_checkpoint(instret, trace_heights);
498 true
499 } else {
500 false
501 }
502 }
503
504 #[inline(always)]
505 fn create_segment_from_checkpoint(&mut self, instret: u64, trace_heights: &mut [u32]) {
506 let instret_start = self
507 .segments
508 .last()
509 .map_or(0, |s| s.instret_start + s.num_insns);
510
511 let (segment_instret, segment_heights) = if self.checkpoint_instret > instret_start {
512 (
513 self.checkpoint_instret,
514 self.checkpoint_trace_heights.clone(),
515 )
516 } else {
517 let trace_heights_str = trace_heights
518 .iter()
519 .zip(self.params.air_names.iter())
520 .filter(|(&height, _)| height > 0)
521 .map(|(&height, name)| format!(" {name} = {height}"))
522 .collect::<Vec<_>>()
523 .join("\n");
524 tracing::warn!(
525 "No valid checkpoint, creating segment using instret={instret}\ntrace_heights=[\n{trace_heights_str}\n]"
526 );
527 (instret, trace_heights.to_vec())
529 };
530
531 let num_insns = segment_instret - instret_start;
532 self.create_segment::<false>(instret_start, num_insns, segment_heights);
533 }
534
535 #[inline(always)]
537 pub(crate) fn initialize_segment(
538 &mut self,
539 trace_heights: &mut [u32],
540 is_trace_height_constant: &[bool],
541 ) {
542 let last_segment = self.segments.last().unwrap();
544 self.reset_trace_heights(
545 trace_heights,
546 &last_segment.trace_heights,
547 is_trace_height_constant,
548 );
549 }
550
551 #[inline(always)]
553 fn reset_trace_heights(
554 &self,
555 trace_heights: &mut [u32],
556 segment_heights: &[u32],
557 is_trace_height_constant: &[bool],
558 ) {
559 for ((trace_height, &segment_height), &is_trace_height_constant) in trace_heights
560 .iter_mut()
561 .zip(segment_heights.iter())
562 .zip(is_trace_height_constant.iter())
563 {
564 if !is_trace_height_constant {
565 *trace_height = trace_height.checked_sub(segment_height).unwrap();
566 }
567 }
568 }
569
570 #[inline(always)]
572 pub(crate) fn update_checkpoint(&mut self, instret: u64, trace_heights: &[u32]) {
573 self.checkpoint_trace_heights.copy_from_slice(trace_heights);
574 self.checkpoint_instret = instret;
575 }
576
577 #[inline(always)]
579 pub fn create_final_segment(&mut self, trace_heights: &[u32]) {
580 self.instret += self.params.segment_check_insns - self.instrets_until_check;
581 self.instrets_until_check = self.params.segment_check_insns;
582 let instret_start = self
583 .segments
584 .last()
585 .map_or(0, |s| s.instret_start + s.num_insns);
586
587 let num_insns = self.instret - instret_start;
588 self.create_segment::<true>(instret_start, num_insns, trace_heights.to_vec());
589 }
590
591 #[inline(always)]
593 fn create_segment<const IS_FINAL: bool>(
594 &mut self,
595 instret_start: u64,
596 num_insns: u64,
597 trace_heights: Vec<u32>,
598 ) {
599 debug_assert!(
600 num_insns > 0,
601 "Segment should contain at least one instruction"
602 );
603
604 self.log_segment_info::<IS_FINAL>(instret_start, num_insns, &trace_heights);
605 #[cfg(feature = "metrics")]
606 {
607 let segment = self.segments.len().to_string();
608 self.emit_metered_segment_metrics(&segment, &trace_heights);
609 self.emit_metered_air_metrics(&segment, &trace_heights);
610 }
611 self.segments.push(Segment {
612 instret_start,
613 num_insns,
614 trace_heights,
615 });
616 }
617
618 #[inline(always)]
622 fn calculate_memory_utilization(&self, trace_heights: &[u32]) -> f64 {
623 let counts = self.calculate_count_breakdown(trace_heights);
624 let memory = self.calculate_memory_breakdown(&counts);
625 if memory.total == 0 {
626 0.0
627 } else {
628 100.0 * memory.unpadded as f64 / memory.total as f64
629 }
630 }
631
632 #[inline(always)]
634 fn log_segment_info<const IS_FINAL: bool>(
635 &self,
636 instret_start: u64,
637 num_insns: u64,
638 trace_heights: &[u32],
639 ) {
640 let (max_trace_height, air_name) = self.calculate_max_trace_height_with_name(trace_heights);
641 let (total_memory, main_memory, interaction_memory) =
642 self.calculate_total_memory(trace_heights);
643 let total_interactions = self.calculate_total_interactions(trace_heights);
644 let utilization = self.calculate_memory_utilization(trace_heights);
645
646 let final_marker = if IS_FINAL { " [TERMINATED]" } else { "" };
647
648 tracing::info!(
649 "Segment {:3} | instret {:10} | {:8} instructions | {:5} memory ({:5}, {:5}) | {:10} interactions | {:8} max height ({}) | {:.2}% memory util{}",
650 self.segments.len(),
651 instret_start,
652 num_insns,
653 ByteSize::b(total_memory as u64),
654 ByteSize::b(main_memory as u64),
655 ByteSize::b(interaction_memory as u64),
656 total_interactions,
657 max_trace_height,
658 air_name,
659 utilization,
660 final_marker
661 );
662 }
663}
664
665#[cfg(feature = "metrics")]
666impl SegmentationCtx {
667 fn emit_segmentation_trigger_metric(&self, trigger: SegmentationTrigger) {
668 let segment = self.segments.len().to_string();
669 let reason = trigger.reason();
670 match trigger {
671 SegmentationTrigger::Height { air_id } => {
672 let labels = [
673 ("segment", segment),
674 ("reason", reason.to_string()),
675 ("air_id", air_id.to_string()),
676 ("air_name", self.params.air_names[air_id].clone()),
677 ];
678 metrics::counter!("segmentation_trigger", &labels).absolute(1);
679 }
680 SegmentationTrigger::Memory | SegmentationTrigger::Interactions => {
681 let labels = [("segment", segment), ("reason", reason.to_string())];
682 metrics::counter!("segmentation_trigger", &labels).absolute(1);
683 }
684 }
685 }
686
687 fn emit_metered_segment_metrics(&self, segment: &str, trace_heights: &[u32]) {
688 let counts = self.calculate_count_breakdown(trace_heights);
689 let memory = self.calculate_memory_breakdown(&counts);
690 let padding = memory.total - memory.unpadded;
691 let estimate = self.params.memory_config.estimate(ProvingMemoryCounts::new(
692 counts.main_unpadded_with_rot + counts.main_padding_with_rot,
693 counts.main_unpadded_no_rot + counts.main_padding_no_rot,
694 counts.interaction_cells_unpadded + counts.interaction_cells_padding,
695 counts.constraint_eval_buffers_unpadded + counts.constraint_eval_buffers_padding,
696 ));
697 let labels = [("segment", segment.to_string())];
698 metrics::counter!("metered_memory_bytes", &labels).absolute(memory.total as u64);
699 metrics::counter!("metered_memory_unpadded_bytes", &labels)
700 .absolute(memory.unpadded as u64);
701 metrics::counter!("metered_memory_padding_bytes", &labels).absolute(padding as u64);
702 metrics::counter!("metered_stacked_matrix_memory_bytes", &labels)
703 .absolute(estimate.stacked_matrix as u64);
704 metrics::counter!("metered_rs_code_matrix_memory_bytes", &labels)
705 .absolute(estimate.rs_code_matrix as u64);
706 metrics::counter!("metered_batch_constraint_memory_bytes", &labels)
707 .absolute(estimate.batch_constraint as u64);
708 metrics::counter!("metered_gkr_memory_bytes", &labels).absolute(estimate.gkr as u64);
709 metrics::counter!("metered_whir_memory_bytes", &labels).absolute(estimate.whir as u64);
710 metrics::counter!("metered_secondary_peak_memory_bytes", &labels)
711 .absolute(estimate.secondary_peak as u64);
712 }
713
714 fn emit_metered_air_metrics(&self, segment: &str, trace_heights: &[u32]) {
715 let memory_config = self.params.memory_config;
716
717 for (air_id, row) in izip!(
718 trace_heights,
719 &self.params.widths,
720 &self.params.interactions,
721 &self.params.constraint_eval_buffers,
722 &self.params.air_names
723 )
724 .enumerate()
725 {
726 let (&height, &width, &interactions, &constraint_eval_buffer, air_name) = row;
727 let padded_height = next_power_of_two_or_zero(height as usize);
728 let unpadded_height = height as usize;
729 let padding_height = padded_height - unpadded_height;
730 if padded_height == 0 {
731 continue;
732 }
733 let labels = [
734 ("air_name", air_name.clone()),
735 ("air_id", air_id.to_string()),
736 ("segment", segment.to_string()),
737 ];
738 let unpadded_cells = unpadded_height * width;
739 let padding_cells = padding_height * width;
740 let interaction_cells_unpadded = unpadded_height * interactions;
742 let interaction_cells_padding = padding_height * interactions;
743 let constraint_eval_cells_unpadded = unpadded_height * constraint_eval_buffer;
745 let constraint_eval_cells_padding = padding_height * constraint_eval_buffer;
746
747 metrics::counter!("metered_rows_unpadded", &labels).absolute(height as u64);
748 metrics::counter!("metered_rows_padding", &labels).absolute(padding_height as u64);
749 metrics::counter!("metered_main_cells_unpadded", &labels)
750 .absolute(unpadded_cells as u64);
751 metrics::counter!("metered_main_cells_padding", &labels).absolute(padding_cells as u64);
752 metrics::counter!("metered_interaction_cells_unpadded", &labels)
753 .absolute(interaction_cells_unpadded as u64);
754 metrics::counter!("metered_interaction_cells_padding", &labels)
755 .absolute(interaction_cells_padding as u64);
756 metrics::counter!("metered_constraint_eval_cells_unpadded", &labels)
757 .absolute(constraint_eval_cells_unpadded as u64);
758 metrics::counter!("metered_constraint_eval_cells_padding", &labels)
759 .absolute(constraint_eval_cells_padding as u64);
760 metrics::counter!("metered_main_memory_unpadded_bytes", &labels)
761 .absolute(memory_config.main_memory_bytes(unpadded_cells) as u64);
762 metrics::counter!("metered_main_memory_padding_bytes", &labels)
763 .absolute(memory_config.main_memory_bytes(padding_cells) as u64);
764 }
765 }
766}