1use std::collections::{BTreeMap, btree_map::Entry};
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum BenchmarkAverageError {
20 ZeroDenominator,
22 Overflow,
24}
25
26impl std::fmt::Display for BenchmarkAverageError {
27 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 formatter.write_str(match self {
29 Self::ZeroDenominator => "benchmark run count is zero",
30 Self::Overflow => "scaled benchmark average exceeds u128",
31 })
32 }
33}
34
35impl std::error::Error for BenchmarkAverageError {}
36
37fn checked_scaled_average(
38 total: u128,
39 runs: u64,
40 scale: u64,
41) -> Result<u128, BenchmarkAverageError> {
42 if runs == 0 {
43 return Err(BenchmarkAverageError::ZeroDenominator);
44 }
45 let runs = u128::from(runs);
46 let scale = u128::from(scale);
47 let fraction = (total % runs) * scale / runs;
50 (total / runs)
51 .checked_mul(scale)
52 .and_then(|whole| whole.checked_add(fraction))
53 .ok_or(BenchmarkAverageError::Overflow)
54}
55
56#[derive(Clone, Debug, Eq, PartialEq)]
58pub enum BenchmarkComparisonError {
59 DuplicateCurrent {
61 scope: BenchmarkScope,
63 span_label: String,
65 },
66 DuplicatePrevious {
68 scope: BenchmarkScope,
70 span_label: String,
72 },
73 Average(BenchmarkAverageError),
75}
76
77impl std::fmt::Display for BenchmarkComparisonError {
78 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 Self::DuplicateCurrent { scope, span_label } => write!(
81 formatter,
82 "duplicate current benchmark row: {scope:?}/{span_label}"
83 ),
84 Self::DuplicatePrevious { scope, span_label } => write!(
85 formatter,
86 "duplicate previous benchmark row: {scope:?}/{span_label}"
87 ),
88 Self::Average(error) => error.fmt(formatter),
89 }
90 }
91}
92impl std::error::Error for BenchmarkComparisonError {
93 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
94 match self {
95 Self::Average(error) => Some(error),
96 Self::DuplicateCurrent { .. } | Self::DuplicatePrevious { .. } => None,
97 }
98 }
99}
100impl From<BenchmarkAverageError> for BenchmarkComparisonError {
101 fn from(error: BenchmarkAverageError) -> Self {
102 Self::Average(error)
103 }
104}
105
106pub const DEFAULT_PREFIX: &str = "ICTK";
108
109#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
111pub enum BenchmarkScope {
112 Suite(String),
114 All,
116}
117
118#[derive(Clone, Debug, Eq, PartialEq)]
120pub struct BenchmarkParserConfig {
121 pub prefixes: Vec<String>,
123 pub suite_derivation: SuiteDerivation,
125 pub strict: bool,
128}
129
130impl Default for BenchmarkParserConfig {
131 fn default() -> Self {
132 Self {
133 prefixes: vec![DEFAULT_PREFIX.to_string()],
134 suite_derivation: SuiteDerivation::FirstPathSegment,
135 strict: false,
136 }
137 }
138}
139
140#[derive(Clone, Debug, Eq, PartialEq)]
142pub enum SuiteDerivation {
143 FirstPathSegment,
145 Fixed(String),
147}
148
149impl SuiteDerivation {
150 #[must_use]
152 pub fn derive_suite(&self, span_label: &str) -> String {
153 match self {
154 Self::FirstPathSegment => span_label
155 .split('/')
156 .next()
157 .filter(|part| !part.is_empty())
158 .unwrap_or(span_label)
159 .to_string(),
160 Self::Fixed(suite) => suite.clone(),
161 }
162 }
163}
164
165#[derive(Clone, Copy, Debug, Eq, PartialEq)]
167pub enum BenchmarkEventKind {
168 Start,
170 End,
172}
173
174#[derive(Clone, Copy, Debug, Eq, PartialEq)]
176pub enum BenchmarkEventSource {
177 Unknown,
179 Stdout,
181 Stderr,
183 FetchedLog,
185}
186
187impl BenchmarkEventSource {
188 #[must_use]
190 pub const fn as_str(self) -> &'static str {
191 match self {
192 Self::Unknown => "unknown",
193 Self::Stdout => "stdout",
194 Self::Stderr => "stderr",
195 Self::FetchedLog => "fetched_log",
196 }
197 }
198}
199
200#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
202pub struct BenchmarkCounters {
203 pub instructions: u128,
205 pub heap_bytes: u128,
207 pub memory_bytes: u128,
209}
210
211impl BenchmarkCounters {
212 fn checked_delta(self, start: Self) -> Option<Self> {
213 Some(Self {
214 instructions: self.instructions.checked_sub(start.instructions)?,
215 heap_bytes: self.heap_bytes.checked_sub(start.heap_bytes)?,
216 memory_bytes: self.memory_bytes.checked_sub(start.memory_bytes)?,
217 })
218 }
219
220 fn checked_add(self, other: Self) -> Result<Self, BenchmarkCounter> {
221 Ok(Self {
222 instructions: self
223 .instructions
224 .checked_add(other.instructions)
225 .ok_or(BenchmarkCounter::Instructions)?,
226 heap_bytes: self
227 .heap_bytes
228 .checked_add(other.heap_bytes)
229 .ok_or(BenchmarkCounter::HeapBytes)?,
230 memory_bytes: self
231 .memory_bytes
232 .checked_add(other.memory_bytes)
233 .ok_or(BenchmarkCounter::MemoryBytes)?,
234 })
235 }
236
237 fn min_assign(&mut self, other: Self) {
238 self.instructions = self.instructions.min(other.instructions);
239 self.heap_bytes = self.heap_bytes.min(other.heap_bytes);
240 self.memory_bytes = self.memory_bytes.min(other.memory_bytes);
241 }
242
243 fn max_assign(&mut self, other: Self) {
244 self.instructions = self.instructions.max(other.instructions);
245 self.heap_bytes = self.heap_bytes.max(other.heap_bytes);
246 self.memory_bytes = self.memory_bytes.max(other.memory_bytes);
247 }
248}
249
250#[derive(Clone, Debug, Eq, PartialEq)]
252pub struct RawBenchmarkEvent {
253 pub prefix: String,
255 pub suite: String,
257 pub span_label: String,
259 pub kind: BenchmarkEventKind,
261 pub counters: BenchmarkCounters,
263 pub source_line: usize,
265 pub source: BenchmarkEventSource,
267}
268
269#[derive(Clone, Debug, Eq, PartialEq)]
271pub struct MalformedBenchmarkMarker {
272 pub source_line: usize,
274 pub source: BenchmarkEventSource,
276 pub line: String,
278 pub reason: String,
280}
281
282#[derive(Clone, Debug, Default, Eq, PartialEq)]
284pub struct BenchmarkParseReport {
285 pub events: Vec<RawBenchmarkEvent>,
287 pub malformed_markers: Vec<MalformedBenchmarkMarker>,
289 pub ignored_line_count: usize,
291}
292
293#[derive(Clone, Debug, Eq, PartialEq)]
295pub struct BenchmarkSpan {
296 pub suite: String,
298 pub span_label: String,
300 pub start_line: usize,
302 pub end_line: usize,
304 pub end: BenchmarkCounters,
306 pub delta: BenchmarkCounters,
308}
309
310#[derive(Clone, Debug, Eq, PartialEq)]
312pub struct InvalidBenchmarkSpan {
313 pub start: RawBenchmarkEvent,
315 pub end: RawBenchmarkEvent,
317}
318
319#[derive(Clone, Debug, Default, Eq, PartialEq)]
321pub struct BenchmarkSpanReport {
322 pub spans: Vec<BenchmarkSpan>,
324 pub unpaired_markers: Vec<RawBenchmarkEvent>,
326 pub invalid_spans: Vec<InvalidBenchmarkSpan>,
328}
329
330#[derive(Clone, Debug, Eq, PartialEq)]
332pub struct BenchmarkAggregateRow {
333 pub span_label: String,
335 pub runs: u64,
337 pub total: BenchmarkCounters,
339 pub min: BenchmarkCounters,
341 pub max: BenchmarkCounters,
343 pub peak_end: BenchmarkCounters,
345 pub scope: BenchmarkScope,
347}
348
349#[derive(Clone, Copy, Debug, Eq, PartialEq)]
354pub struct BenchmarkAverages {
355 pub scale: u64,
357 pub instructions: u128,
359 pub heap_bytes: u128,
361 pub memory_bytes: u128,
363}
364
365#[derive(Clone, Copy, Debug, Eq, PartialEq)]
367pub enum BenchmarkCounter {
368 Runs,
370 Instructions,
372 HeapBytes,
374 MemoryBytes,
376}
377
378#[derive(Clone, Debug, Eq, PartialEq)]
380pub struct BenchmarkAggregateError {
381 pub span_label: String,
383 pub counter: BenchmarkCounter,
385 pub scope: BenchmarkScope,
387}
388
389impl std::fmt::Display for BenchmarkAggregateError {
390 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 write!(
392 formatter,
393 "benchmark aggregate overflow: scope={:?}, span={}, counter={:?}",
394 self.scope, self.span_label, self.counter
395 )
396 }
397}
398
399impl std::error::Error for BenchmarkAggregateError {}
400
401#[derive(Clone, Copy, Debug, Eq, PartialEq)]
403pub struct BenchmarkMeasurement {
404 pub runs: u64,
406 pub average: BenchmarkAverages,
408}
409
410#[derive(Clone, Debug, Eq, PartialEq)]
412pub struct BenchmarkComparisonRow {
413 pub span_label: String,
415 pub current: Option<BenchmarkMeasurement>,
417 pub previous: Option<BenchmarkMeasurement>,
419 pub scope: BenchmarkScope,
421}
422
423impl BenchmarkAggregateRow {
424 pub fn average(&self, scale: u64) -> Result<BenchmarkAverages, BenchmarkAverageError> {
429 Ok(BenchmarkAverages {
430 scale,
431 instructions: checked_scaled_average(self.total.instructions, self.runs, scale)?,
432 heap_bytes: checked_scaled_average(self.total.heap_bytes, self.runs, scale)?,
433 memory_bytes: checked_scaled_average(self.total.memory_bytes, self.runs, scale)?,
434 })
435 }
436}
437
438#[must_use]
443pub fn format_marker(prefix: &str, label: &str, counters: BenchmarkCounters) -> String {
444 format!(
445 "{}|{}|{}|{}|{}",
446 prefix, label, counters.instructions, counters.heap_bytes, counters.memory_bytes
447 )
448}
449
450#[derive(Clone, Copy, Debug, Eq, PartialEq)]
452pub enum BenchmarkMarkerError {
453 InvalidPrefix,
455 InvalidLabel,
457}
458
459impl std::fmt::Display for BenchmarkMarkerError {
460 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461 f.write_str(match self {
462 Self::InvalidPrefix => "invalid benchmark marker prefix",
463 Self::InvalidLabel => "invalid benchmark marker label",
464 })
465 }
466}
467
468impl std::error::Error for BenchmarkMarkerError {}
469
470pub fn try_format_marker(
479 prefix: &str,
480 label: &str,
481 counters: BenchmarkCounters,
482) -> Result<String, BenchmarkMarkerError> {
483 if prefix.is_empty() || prefix.contains(['|', '\r', '\n']) {
484 return Err(BenchmarkMarkerError::InvalidPrefix);
485 }
486 if label.contains(['|', '\r', '\n']) || split_label_kind(label).is_none() {
487 return Err(BenchmarkMarkerError::InvalidLabel);
488 }
489 Ok(format_marker(prefix, label, counters))
490}
491
492#[must_use]
494pub fn parse_benchmark_events(input: &str, config: &BenchmarkParserConfig) -> BenchmarkParseReport {
495 parse_benchmark_events_from_source(input, config, BenchmarkEventSource::Unknown)
496}
497
498#[must_use]
500pub fn parse_benchmark_events_from_source(
501 input: &str,
502 config: &BenchmarkParserConfig,
503 source: BenchmarkEventSource,
504) -> BenchmarkParseReport {
505 let mut report = BenchmarkParseReport::default();
506 append_benchmark_events(&mut report, input, config, source);
507 report
508}
509
510fn append_benchmark_events(
511 report: &mut BenchmarkParseReport,
512 input: &str,
513 config: &BenchmarkParserConfig,
514 source: BenchmarkEventSource,
515) {
516 for (index, line) in input.lines().enumerate() {
517 let source_line = index + 1;
518 if !has_configured_prefix(line, &config.prefixes) {
519 if config.strict && !line.trim().is_empty() {
520 report.malformed_markers.push(malformed(
521 source_line,
522 source,
523 line,
524 "line does not use a configured marker prefix",
525 ));
526 } else {
527 report.ignored_line_count += 1;
528 }
529 continue;
530 }
531
532 match parse_marker_line(line, source_line, source, config) {
533 Ok(event) => report.events.push(event),
534 Err(marker) => report.malformed_markers.push(marker),
535 }
536 }
537}
538
539#[must_use]
545pub fn parse_benchmark_events_from_captured_output(
546 stdout: &str,
547 stderr: &str,
548 config: &BenchmarkParserConfig,
549) -> BenchmarkParseReport {
550 let mut report = BenchmarkParseReport::default();
551 append_benchmark_events(&mut report, stdout, config, BenchmarkEventSource::Stdout);
552 append_benchmark_events(&mut report, stderr, config, BenchmarkEventSource::Stderr);
553 report
554}
555
556#[must_use]
558pub fn pair_benchmark_spans(events: &[RawBenchmarkEvent]) -> BenchmarkSpanReport {
559 pair_spans(events, |event| (&event.suite, &event.span_label))
560}
561
562#[must_use]
570pub fn pair_benchmark_spans_by_origin(events: &[RawBenchmarkEvent]) -> BenchmarkSpanReport {
571 pair_spans(events, |event| {
572 (
573 &event.prefix,
574 event.source.as_str(),
575 &event.suite,
576 &event.span_label,
577 )
578 })
579}
580
581fn pair_spans<'a, K: Ord>(
582 events: &'a [RawBenchmarkEvent],
583 key: impl Fn(&'a RawBenchmarkEvent) -> K,
584) -> BenchmarkSpanReport {
585 let mut report = BenchmarkSpanReport::default();
586 let mut open_starts = BTreeMap::<_, Vec<&RawBenchmarkEvent>>::new();
587
588 for event in events {
589 let key = key(event);
590 match event.kind {
591 BenchmarkEventKind::Start => open_starts.entry(key).or_default().push(event),
592 BenchmarkEventKind::End => match open_starts.entry(key) {
593 Entry::Occupied(mut entry) => {
594 let start = entry
595 .get_mut()
596 .pop()
597 .expect("open marker stacks must remain nonempty");
598 if entry.get().is_empty() {
599 entry.remove();
600 }
601 push_paired_span(&mut report, start, event);
602 }
603 Entry::Vacant(_) => report.unpaired_markers.push(event.clone()),
604 },
605 }
606 }
607
608 for starts in open_starts.into_values() {
609 for event in starts {
610 report.unpaired_markers.push(event.clone());
611 }
612 }
613
614 report
615}
616
617pub fn aggregate_benchmark_spans(
623 spans: &[BenchmarkSpan],
624) -> Result<Vec<BenchmarkAggregateRow>, BenchmarkAggregateError> {
625 let mut rows = BTreeMap::new();
626
627 for span in spans {
628 add_span_to_aggregate(&mut rows, AggregateScope::Suite(&span.suite), span)?;
629 add_span_to_aggregate(&mut rows, AggregateScope::All, span)?;
630 }
631
632 Ok(rows
633 .into_iter()
634 .map(|((scope, span_label), builder)| {
635 builder.finish(scope.into_owned(), span_label.to_owned())
636 })
637 .collect())
638}
639
640pub fn compare_benchmark_aggregates(
645 current: &[BenchmarkAggregateRow],
646 previous: &[BenchmarkAggregateRow],
647 scale: u64,
648) -> Result<Vec<BenchmarkComparisonRow>, BenchmarkComparisonError> {
649 let mut rows = BTreeMap::new();
650 for row in current {
651 if rows
652 .insert((&row.scope, row.span_label.as_str()), (Some(row), None))
653 .is_some()
654 {
655 return Err(BenchmarkComparisonError::DuplicateCurrent {
656 scope: row.scope.clone(),
657 span_label: row.span_label.clone(),
658 });
659 }
660 }
661 for row in previous {
662 let previous = &mut rows
663 .entry((&row.scope, row.span_label.as_str()))
664 .or_insert((None, None))
665 .1;
666 if previous.replace(row).is_some() {
667 return Err(BenchmarkComparisonError::DuplicatePrevious {
668 scope: row.scope.clone(),
669 span_label: row.span_label.clone(),
670 });
671 }
672 }
673 let measurement = |row: &BenchmarkAggregateRow| -> Result<_, BenchmarkAverageError> {
674 Ok(BenchmarkMeasurement {
675 runs: row.runs,
676 average: row.average(scale)?,
677 })
678 };
679
680 rows.into_iter()
681 .map(|((scope, span_label), (current_row, previous_row))| {
682 Ok(BenchmarkComparisonRow {
683 span_label: span_label.to_owned(),
684 current: current_row.map(measurement).transpose()?,
685 previous: previous_row.map(measurement).transpose()?,
686 scope: scope.clone(),
687 })
688 })
689 .collect()
690}
691
692fn parse_marker_line(
693 line: &str,
694 source_line: usize,
695 source: BenchmarkEventSource,
696 config: &BenchmarkParserConfig,
697) -> Result<RawBenchmarkEvent, MalformedBenchmarkMarker> {
698 let invalid = |reason: String| malformed(source_line, source, line, reason);
699 let parse_counter = |value: &str, name: &str| {
700 if value.is_empty() {
701 return Err(invalid(format!("{name} counter is empty")));
702 }
703 value
704 .parse::<u128>()
705 .map_err(|_| invalid(format!("{name} counter is not an unsigned integer")))
706 };
707 let mut parts = line.split('|');
708 let columns: [Option<&str>; 6] = std::array::from_fn(|_| parts.next());
711 let [
712 Some(prefix),
713 Some(label),
714 Some(instructions),
715 Some(heap_bytes),
716 Some(memory_bytes),
717 None,
718 ] = columns
719 else {
720 return Err(invalid("expected five pipe-separated columns".into()));
721 };
722
723 if !config.prefixes.iter().any(|known| known == prefix) {
724 return Err(invalid("prefix is not configured".into()));
725 }
726
727 if label.is_empty() {
728 return Err(invalid("label is empty".into()));
729 }
730
731 let (span_label, kind) = split_label_kind(label)
732 .ok_or_else(|| invalid("label must end in :start or :end".into()))?;
733
734 let counters = BenchmarkCounters {
735 instructions: parse_counter(instructions, "instructions")?,
736 heap_bytes: parse_counter(heap_bytes, "heap_bytes")?,
737 memory_bytes: parse_counter(memory_bytes, "memory_bytes")?,
738 };
739 let suite = config.suite_derivation.derive_suite(span_label);
740
741 Ok(RawBenchmarkEvent {
742 prefix: prefix.to_string(),
743 suite,
744 span_label: span_label.to_string(),
745 kind,
746 counters,
747 source_line,
748 source,
749 })
750}
751
752fn split_label_kind(label: &str) -> Option<(&str, BenchmarkEventKind)> {
753 let start = label.strip_suffix(":start");
754 let end = label.strip_suffix(":end");
755
756 match (start, end) {
757 (Some(span_label), None) if !span_label.is_empty() => {
758 Some((span_label, BenchmarkEventKind::Start))
759 }
760 (None, Some(span_label)) if !span_label.is_empty() => {
761 Some((span_label, BenchmarkEventKind::End))
762 }
763 _ => None,
764 }
765}
766
767fn has_configured_prefix(line: &str, prefixes: &[String]) -> bool {
768 prefixes.iter().any(|prefix| {
769 line.strip_prefix(prefix)
770 .is_some_and(|rest| rest.starts_with('|'))
771 })
772}
773
774fn malformed(
775 source_line: usize,
776 source: BenchmarkEventSource,
777 line: &str,
778 reason: impl Into<String>,
779) -> MalformedBenchmarkMarker {
780 MalformedBenchmarkMarker {
781 source_line,
782 source,
783 line: line.to_string(),
784 reason: reason.into(),
785 }
786}
787
788fn push_paired_span(
789 report: &mut BenchmarkSpanReport,
790 start: &RawBenchmarkEvent,
791 end: &RawBenchmarkEvent,
792) {
793 if let Some(delta) = end.counters.checked_delta(start.counters) {
794 report.spans.push(BenchmarkSpan {
795 suite: start.suite.clone(),
796 span_label: start.span_label.clone(),
797 start_line: start.source_line,
798 end_line: end.source_line,
799 end: end.counters,
800 delta,
801 });
802 } else {
803 report.invalid_spans.push(InvalidBenchmarkSpan {
804 start: start.clone(),
805 end: end.clone(),
806 });
807 }
808}
809
810#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
813enum AggregateScope<'a> {
814 Suite(&'a str),
815 All,
816}
817
818impl AggregateScope<'_> {
819 fn into_owned(self) -> BenchmarkScope {
820 match self {
821 Self::Suite(suite) => BenchmarkScope::Suite(suite.to_owned()),
822 Self::All => BenchmarkScope::All,
823 }
824 }
825}
826
827#[derive(Clone, Debug)]
828struct AggregateBuilder {
829 runs: u64,
830 total: BenchmarkCounters,
831 min: BenchmarkCounters,
832 max: BenchmarkCounters,
833 peak_end: BenchmarkCounters,
834}
835
836impl AggregateBuilder {
837 const fn new(span: &BenchmarkSpan) -> Self {
838 Self {
839 runs: 1,
840 total: span.delta,
841 min: span.delta,
842 max: span.delta,
843 peak_end: span.end,
844 }
845 }
846
847 fn push(&mut self, span: &BenchmarkSpan) -> Result<(), BenchmarkCounter> {
848 let runs = self.runs.checked_add(1).ok_or(BenchmarkCounter::Runs)?;
849 let total = self.total.checked_add(span.delta)?;
850 self.runs = runs;
851 self.total = total;
852 self.min.min_assign(span.delta);
853 self.max.max_assign(span.delta);
854 self.peak_end.max_assign(span.end);
855 Ok(())
856 }
857
858 const fn finish(self, scope: BenchmarkScope, span_label: String) -> BenchmarkAggregateRow {
859 BenchmarkAggregateRow {
860 span_label,
861 runs: self.runs,
862 total: self.total,
863 min: self.min,
864 max: self.max,
865 peak_end: self.peak_end,
866 scope,
867 }
868 }
869}
870
871fn add_span_to_aggregate<'a>(
872 rows: &mut BTreeMap<(AggregateScope<'a>, &'a str), AggregateBuilder>,
873 scope: AggregateScope<'a>,
874 span: &'a BenchmarkSpan,
875) -> Result<(), BenchmarkAggregateError> {
876 match rows.entry((scope, span.span_label.as_str())) {
877 Entry::Occupied(mut entry) => entry.get_mut().push(span).map_err(|counter| {
878 let (scope, span_label) = entry.key();
879 BenchmarkAggregateError {
880 span_label: (*span_label).to_owned(),
881 counter,
882 scope: scope.into_owned(),
883 }
884 }),
885 Entry::Vacant(entry) => {
886 entry.insert(AggregateBuilder::new(span));
887 Ok(())
888 }
889 }
890}
891
892#[cfg(test)]
893mod tests {
894 use super::{AggregateBuilder, BenchmarkCounter, BenchmarkCounters, BenchmarkSpan};
895
896 #[test]
897 fn aggregate_run_count_overflow_is_rejected_without_changing_totals() {
898 let span = BenchmarkSpan {
899 suite: "probe".into(),
900 span_label: "probe/run".into(),
901 start_line: 1,
902 end_line: 2,
903 end: BenchmarkCounters::default(),
904 delta: BenchmarkCounters::default(),
905 };
906 let mut builder = AggregateBuilder::new(&span);
907 builder.runs = u64::MAX;
908 assert_eq!(builder.push(&span), Err(BenchmarkCounter::Runs));
909 assert_eq!(builder.runs, u64::MAX);
910 assert_eq!(builder.total, BenchmarkCounters::default());
911 }
912}