1use crate::prelude::*;
29
30use num_traits::FromPrimitive;
31use std::sync::atomic::AtomicU64;
32use std::time::Duration;
33
34#[derive(Default)]
40pub struct DBPerf<T> {
41 chip: T,
43 perf_counters: PerfCounters,
44}
45
46struct PerfCounters {
47 perf_counters: Vec<(FnName, PerfCounter)>,
48}
49
50impl Default for PerfCounters {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl<T> DBPerf<T> {
57 pub fn new(chip: T) -> Self {
59 Self {
60 chip,
61 perf_counters: PerfCounters::new(),
62 }
63 }
64
65 pub fn get_stats_all(&self) -> Vec<(FnName, PerfCounterResult)> {
67 self.perf_counters
68 .perf_counters
69 .iter()
70 .map(|(fname, counter)| (*fname, counter.atomic_read()))
71 .collect()
72 }
73
74 pub fn get_stats(&self, function_name: FnName) -> PerfCounterResult {
76 self.perf_counters.get(function_name).atomic_read()
77 }
78
79 pub fn into_inner(self) -> T {
82 self.chip
83 }
84}
85
86impl PerfCounters {
87 fn new() -> Self {
88 Self {
89 perf_counters: (0..)
90 .map(FnName::from_usize)
91 .take_while(|fname| fname.is_some())
92 .map(|fname| (fname.unwrap(), Default::default()))
93 .collect(),
94 }
95 }
96
97 fn get(&self, function_name: FnName) -> &PerfCounter {
99 &self.perf_counters[function_name as usize].1
100 }
101}
102
103#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive, FromPrimitive, Debug)]
106#[allow(non_camel_case_types, missing_docs)]
107pub enum FnName {
108 cell_by_name = 0,
110 cell_instance_by_name,
111 cell_name,
112 cell_instance_name,
113 parent_cell,
114 template_cell,
115 for_each_cell,
116 each_cell_vec,
117 each_cell,
118 for_each_cell_instance,
119 each_cell_instance_vec,
120 each_cell_instance,
121 for_each_cell_dependency,
122 each_cell_dependency_vec,
123 each_cell_dependency,
124 num_cell_dependencies,
125 for_each_dependent_cell,
126 each_dependent_cell_vec,
127 each_dependent_cell,
128 num_dependent_cells,
129 for_each_cell_reference,
130 each_cell_reference_vec,
131 each_cell_reference,
132 num_cell_references,
133 num_child_instances,
134 num_cells,
135 get_chip_property,
136 get_cell_property,
137 get_cell_instance_property,
138
139 create_cell,
141 remove_cell,
142 create_cell_instance,
143 remove_cell_instance,
144 rename_cell_instance,
145 rename_cell,
146 set_chip_property,
147 set_cell_property,
148 set_cell_instance_property,
149
150 insert_shape,
152 set_dbu,
153 create_layer,
154 create_layer_with_id,
155 set_layer_name,
156 remove_shape,
157 replace_shape,
158 set_transform,
159 set_shape_property,
160
161 create_pin,
163 remove_pin,
164 rename_pin,
165 create_net,
166 rename_net,
167 remove_net,
168 connect_pin,
169 connect_pin_instance,
170 disconnect_pin,
171 disconnect_pin_instance,
172
173 shapes_of_net,
175 shapes_of_pin,
176 get_net_of_shape,
177 get_pin_of_shape,
178
179 set_pin_of_shape,
181 set_net_of_shape,
182}
183
184impl std::fmt::Display for FnName {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(f, "{:?}", self)
188 }
189}
190
191#[derive(Debug)]
193pub struct PerfCounter {
194 num_calls: AtomicU64,
196 total_time_ns: AtomicU64,
198 min_time_ns: AtomicU64,
200 max_time_ns: AtomicU64,
202}
203
204#[derive(Debug, Copy, Clone, PartialEq, Eq)]
206pub struct PerfCounterResult {
207 pub num_calls: u64,
209 pub total_time: Duration,
211 pub min_time: Duration,
213 pub max_time: Duration,
215}
216
217impl PerfCounterResult {
218 pub fn avg_time(&self) -> Option<Duration> {
221 (self.num_calls > 0).then(|| self.total_time / (self.num_calls as u32))
222 }
223}
224
225impl PerfCounter {
226 pub fn atomic_read(&self) -> PerfCounterResult {
228 use std::sync::atomic::Ordering::Relaxed;
229
230 loop {
231 let read = || PerfCounterResult {
232 num_calls: self.num_calls.load(Relaxed),
233 total_time: Duration::from_nanos(self.total_time_ns.load(Relaxed)),
234 min_time: Duration::from_nanos(self.min_time_ns.load(Relaxed)),
235 max_time: Duration::from_nanos(self.max_time_ns.load(Relaxed)),
236 };
237
238 let r = read();
239 if r == read() {
240 break r;
242 }
243
244 std::hint::spin_loop();
245 }
246 }
247}
248
249impl Default for PerfCounter {
250 fn default() -> Self {
251 Self {
252 num_calls: Default::default(),
253 total_time_ns: Default::default(),
254 min_time_ns: AtomicU64::new(u64::MAX),
255 max_time_ns: Default::default(),
256 }
257 }
258}
259
260pub struct PerfCounterManager<'a> {
263 start_time: std::time::Instant,
265 counter: &'a PerfCounter,
266}
267
268impl<'a> PerfCounterManager<'a> {
269 fn stop_measurement(self) {
271 let elapsed = self.start_time.elapsed();
272 let elapsed_ns = elapsed.as_nanos() as u64;
273 use std::sync::atomic::Ordering::*;
274 self.counter.total_time_ns.fetch_add(elapsed_ns, Relaxed);
275 self.counter.min_time_ns.fetch_min(elapsed_ns, Relaxed);
276 self.counter.max_time_ns.fetch_max(elapsed_ns, Relaxed);
277 self.counter.num_calls.fetch_add(1, Relaxed);
278 }
279}
280
281impl PerfCounter {
282 #[must_use]
285 fn start_measurement(&self) -> PerfCounterManager {
286 PerfCounterManager {
287 start_time: std::time::Instant::now(),
288 counter: self,
289 }
290 }
291
292 fn measure<R>(&self, f: impl FnOnce() -> R) -> R {
294 let m = self.start_measurement();
295 let r = f();
296 m.stop_measurement();
297 r
298 }
299}
300
301#[portrait::fill(portrait::delegate(H))]
302impl<H: HierarchyIds> HierarchyIds for DBPerf<H> {}
303
304#[portrait::fill(portrait::delegate(N))]
305impl<N: NetlistIds> NetlistIds for DBPerf<N> {}
306
307impl<H: HierarchyBase> HierarchyBase for DBPerf<H> {
309 type NameType = H::NameType;
310
311 fn cell_by_name(&self, name: &str) -> Option<H::CellId> {
312 self.perf_counters
313 .get(FnName::cell_by_name)
314 .measure(|| self.chip.cell_by_name(name))
315 }
316
317 fn cell_instance_by_name(&self, parent_cell: &H::CellId, name: &str) -> Option<H::CellInstId> {
318 self.perf_counters
319 .get(FnName::cell_instance_by_name)
320 .measure(|| self.chip.cell_instance_by_name(parent_cell, name))
321 }
322
323 fn cell_name(&self, cell: &H::CellId) -> H::NameType {
324 self.perf_counters
325 .get(FnName::cell_name)
326 .measure(|| self.chip.cell_name(cell))
327 }
328
329 fn cell_instance_name(&self, cell_inst: &H::CellInstId) -> Option<H::NameType> {
330 self.perf_counters
331 .get(FnName::cell_instance_name)
332 .measure(|| self.chip.cell_instance_name(cell_inst))
333 }
334
335 fn parent_cell(&self, cell_instance: &H::CellInstId) -> H::CellId {
336 self.perf_counters
337 .get(FnName::parent_cell)
338 .measure(|| self.chip.parent_cell(cell_instance))
339 }
340
341 fn template_cell(&self, cell_instance: &H::CellInstId) -> H::CellId {
342 self.perf_counters
343 .get(FnName::template_cell)
344 .measure(|| self.chip.template_cell(cell_instance))
345 }
346
347 fn for_each_cell<F>(&self, f: F)
348 where
349 F: FnMut(H::CellId),
350 {
351 self.perf_counters
352 .get(FnName::for_each_cell)
353 .measure(|| self.chip.for_each_cell(f))
354 }
355
356 fn each_cell_vec(&self) -> Vec<H::CellId> {
357 self.perf_counters
358 .get(FnName::each_cell_vec)
359 .measure(|| self.chip.each_cell_vec())
360 }
361
362 fn each_cell(&self) -> Box<dyn Iterator<Item = H::CellId> + '_> {
363 self.perf_counters
364 .get(FnName::each_cell)
365 .measure(|| self.chip.each_cell())
366 }
367
368 fn for_each_cell_instance<F>(&self, cell: &H::CellId, f: F)
369 where
370 F: FnMut(H::CellInstId),
371 {
372 self.perf_counters
373 .get(FnName::each_cell_instance)
374 .measure(|| self.chip.for_each_cell_instance(cell, f))
375 }
376
377 fn each_cell_instance_vec(&self, cell: &H::CellId) -> Vec<H::CellInstId> {
378 self.perf_counters
379 .get(FnName::each_cell_instance_vec)
380 .measure(|| self.chip.each_cell_instance_vec(cell))
381 }
382
383 fn each_cell_instance(&self, cell: &H::CellId) -> Box<dyn Iterator<Item = H::CellInstId> + '_> {
384 self.perf_counters
385 .get(FnName::each_cell_instance)
386 .measure(|| self.chip.each_cell_instance(cell))
387 }
388
389 fn for_each_cell_dependency<F>(&self, cell: &H::CellId, f: F)
390 where
391 F: FnMut(H::CellId),
392 {
393 self.perf_counters
394 .get(FnName::for_each_cell_dependency)
395 .measure(|| self.chip.for_each_cell_dependency(cell, f))
396 }
397
398 fn each_cell_dependency_vec(&self, cell: &H::CellId) -> Vec<H::CellId> {
399 self.perf_counters
400 .get(FnName::each_cell_dependency_vec)
401 .measure(|| self.chip.each_cell_dependency_vec(cell))
402 }
403
404 fn each_cell_dependency(&self, cell: &H::CellId) -> Box<dyn Iterator<Item = H::CellId> + '_> {
405 self.perf_counters
406 .get(FnName::each_cell_dependency)
407 .measure(|| self.chip.each_cell_dependency(cell))
408 }
409
410 fn num_cell_dependencies(&self, cell: &H::CellId) -> usize {
411 self.perf_counters
412 .get(FnName::num_cell_dependencies)
413 .measure(|| self.chip.num_cell_dependencies(cell))
414 }
415
416 fn for_each_dependent_cell<F>(&self, cell: &H::CellId, f: F)
417 where
418 F: FnMut(H::CellId),
419 {
420 self.perf_counters
421 .get(FnName::for_each_dependent_cell)
422 .measure(|| self.chip.for_each_dependent_cell(cell, f))
423 }
424
425 fn each_dependent_cell_vec(&self, cell: &H::CellId) -> Vec<H::CellId> {
426 self.perf_counters
427 .get(FnName::each_dependent_cell_vec)
428 .measure(|| self.chip.each_dependent_cell_vec(cell))
429 }
430
431 fn each_dependent_cell(&self, cell: &H::CellId) -> Box<dyn Iterator<Item = H::CellId> + '_> {
432 self.perf_counters
433 .get(FnName::each_dependent_cell)
434 .measure(|| self.chip.each_dependent_cell(cell))
435 }
436
437 fn num_dependent_cells(&self, cell: &H::CellId) -> usize {
438 self.perf_counters
439 .get(FnName::num_dependent_cells)
440 .measure(|| self.chip.num_dependent_cells(cell))
441 }
442
443 fn for_each_cell_reference<F>(&self, cell: &H::CellId, f: F)
444 where
445 F: FnMut(H::CellInstId),
446 {
447 self.perf_counters
448 .get(FnName::for_each_cell_reference)
449 .measure(|| self.chip.for_each_cell_reference(cell, f))
450 }
451
452 fn each_cell_reference_vec(&self, cell: &H::CellId) -> Vec<H::CellInstId> {
453 self.perf_counters
454 .get(FnName::each_cell_reference_vec)
455 .measure(|| self.chip.each_cell_reference_vec(cell))
456 }
457
458 fn each_cell_reference(
459 &self,
460 cell: &H::CellId,
461 ) -> Box<dyn Iterator<Item = H::CellInstId> + '_> {
462 self.perf_counters
463 .get(FnName::each_cell_reference)
464 .measure(|| self.chip.each_cell_reference(cell))
465 }
466
467 fn num_cell_references(&self, cell: &H::CellId) -> usize {
468 self.perf_counters
469 .get(FnName::num_cell_references)
470 .measure(|| self.chip.num_cell_references(cell))
471 }
472
473 fn num_child_instances(&self, cell: &H::CellId) -> usize {
474 self.perf_counters
475 .get(FnName::num_child_instances)
476 .measure(|| self.chip.num_child_instances(cell))
477 }
478
479 fn num_cells(&self) -> usize {
480 self.perf_counters
481 .get(FnName::num_cells)
482 .measure(|| self.chip.num_cells())
483 }
484
485 fn get_chip_property(&self, key: &H::NameType) -> Option<PropertyValue> {
486 self.perf_counters
487 .get(FnName::get_chip_property)
488 .measure(|| self.chip.get_chip_property(key))
489 }
490
491 fn get_cell_property(&self, cell: &H::CellId, key: &H::NameType) -> Option<PropertyValue> {
492 self.perf_counters
493 .get(FnName::get_cell_property)
494 .measure(|| self.chip.get_cell_property(cell, key))
495 }
496
497 fn get_cell_instance_property(
498 &self,
499 inst: &H::CellInstId,
500 key: &H::NameType,
501 ) -> Option<PropertyValue> {
502 self.perf_counters
503 .get(FnName::get_cell_instance_property)
504 .measure(|| self.chip.get_cell_instance_property(inst, key))
505 }
506}
507
508#[portrait::fill(portrait::delegate(L))]
510impl<L: LayoutIds> LayoutIds for DBPerf<L> {}
511
512#[portrait::fill(portrait::delegate(L; self.chip))]
514impl<L: LayoutBase> LayoutBase for DBPerf<L> {}
515
516#[portrait::fill(portrait::delegate(N; self.chip))]
518impl<N: NetlistBase> NetlistBase for DBPerf<N> {}
519
520impl<H: HierarchyEdit> HierarchyEdit for DBPerf<H> {
522 fn create_cell(&mut self, name: H::NameType) -> H::CellId {
523 self.perf_counters
524 .get(FnName::create_cell)
525 .measure(|| self.chip.create_cell(name))
526 }
527
528 fn remove_cell(&mut self, cell_id: &H::CellId) {
529 self.perf_counters
530 .get(FnName::remove_cell)
531 .measure(|| self.chip.remove_cell(cell_id))
532 }
533
534 fn create_cell_instance(
535 &mut self,
536 parent_cell: &H::CellId,
537 template_cell: &H::CellId,
538 name: Option<H::NameType>,
539 ) -> H::CellInstId {
540 self.perf_counters
541 .get(FnName::create_cell_instance)
542 .measure(|| {
543 self.chip
544 .create_cell_instance(parent_cell, template_cell, name)
545 })
546 }
547
548 fn remove_cell_instance(&mut self, inst: &H::CellInstId) {
549 self.perf_counters
550 .get(FnName::remove_cell_instance)
551 .measure(|| self.chip.remove_cell_instance(inst))
552 }
553
554 fn rename_cell_instance(&mut self, inst: &H::CellInstId, new_name: Option<H::NameType>) {
555 self.perf_counters
556 .get(FnName::rename_cell_instance)
557 .measure(|| self.chip.rename_cell_instance(inst, new_name))
558 }
559
560 fn rename_cell(&mut self, cell: &H::CellId, new_name: H::NameType) {
561 self.perf_counters
562 .get(FnName::rename_cell)
563 .measure(|| self.chip.rename_cell(cell, new_name))
564 }
565
566 fn set_chip_property(&mut self, key: H::NameType, value: PropertyValue) {
567 self.perf_counters
568 .get(FnName::set_chip_property)
569 .measure(|| self.chip.set_chip_property(key, value))
570 }
571
572 fn set_cell_property(&mut self, cell: &H::CellId, key: H::NameType, value: PropertyValue) {
573 self.perf_counters
574 .get(FnName::set_cell_property)
575 .measure(|| self.chip.set_cell_property(cell, key, value))
576 }
577
578 fn set_cell_instance_property(
579 &mut self,
580 inst: &H::CellInstId,
581 key: H::NameType,
582 value: PropertyValue,
583 ) {
584 self.perf_counters
585 .get(FnName::set_cell_instance_property)
586 .measure(|| self.chip.set_cell_instance_property(inst, key, value))
587 }
588}
589
590impl<L: LayoutEdit> LayoutEdit for DBPerf<L> {
592 fn insert_shape(
593 &mut self,
594 parent_cell: &L::CellId,
595 layer: &L::LayerId,
596 geometry: Geometry<L::Coord>,
597 ) -> L::ShapeId {
598 self.perf_counters
599 .get(FnName::insert_shape)
600 .measure(|| self.chip.insert_shape(parent_cell, layer, geometry))
601 }
602
603 fn set_dbu(&mut self, dbu: L::Coord) {
604 self.perf_counters
605 .get(FnName::set_dbu)
606 .measure(|| self.chip.set_dbu(dbu))
607 }
608
609 fn create_layer(&mut self, index: UInt, datatype: UInt) -> L::LayerId {
610 self.perf_counters
611 .get(FnName::create_layer)
612 .measure(|| self.chip.create_layer(index, datatype))
613 }
614
615 fn create_layer_with_id(
616 &mut self,
617 layer_id: L::LayerId,
618 index: UInt,
619 datatype: UInt,
620 ) -> Result<(), ()> {
621 self.perf_counters
622 .get(FnName::create_layer_with_id)
623 .measure(|| self.chip.create_layer_with_id(layer_id, index, datatype))
624 }
625
626 fn set_layer_name(
627 &mut self,
628 layer: &L::LayerId,
629 name: Option<L::NameType>,
630 ) -> Option<L::NameType> {
631 self.perf_counters
632 .get(FnName::set_layer_name)
633 .measure(|| self.chip.set_layer_name(layer, name))
634 }
635
636 fn remove_shape(&mut self, shape_id: &L::ShapeId) -> Option<Geometry<L::Coord>> {
637 self.perf_counters
638 .get(FnName::remove_shape)
639 .measure(|| self.chip.remove_shape(shape_id))
640 }
641
642 fn replace_shape(
643 &mut self,
644 shape_id: &L::ShapeId,
645 geometry: Geometry<L::Coord>,
646 ) -> Geometry<L::Coord> {
647 self.perf_counters
648 .get(FnName::replace_shape)
649 .measure(|| self.chip.replace_shape(shape_id, geometry))
650 }
651
652 fn set_transform(&mut self, cell_inst: &L::CellInstId, tf: SimpleTransform<L::Coord>) {
653 self.perf_counters
654 .get(FnName::set_transform)
655 .measure(|| self.chip.set_transform(cell_inst, tf))
656 }
657
658 fn set_shape_property(&mut self, shape: &L::ShapeId, key: L::NameType, value: PropertyValue) {
659 self.perf_counters
660 .get(FnName::set_shape_property)
661 .measure(|| self.chip.set_shape_property(shape, key, value))
662 }
663}
664
665impl<N: NetlistEdit> NetlistEdit for DBPerf<N> {
666 fn create_pin(
667 &mut self,
668 cell: &Self::CellId,
669 name: Self::NameType,
670 direction: Direction,
671 ) -> Self::PinId {
672 self.perf_counters
673 .get(FnName::create_pin)
674 .measure(|| self.chip.create_pin(cell, name, direction))
675 }
676
677 fn remove_pin(&mut self, id: &Self::PinId) {
678 self.perf_counters
679 .get(FnName::remove_pin)
680 .measure(|| self.chip.remove_pin(id))
681 }
682
683 fn rename_pin(&mut self, pin: &Self::PinId, new_name: Self::NameType) -> Self::NameType {
684 self.perf_counters
685 .get(FnName::rename_pin)
686 .measure(|| self.chip.rename_pin(pin, new_name))
687 }
688
689 fn create_net(&mut self, parent: &Self::CellId, name: Option<Self::NameType>) -> Self::NetId {
690 self.perf_counters
691 .get(FnName::create_net)
692 .measure(|| self.chip.create_net(parent, name))
693 }
694
695 fn rename_net(
696 &mut self,
697 net_id: &Self::NetId,
698 new_name: Option<Self::NameType>,
699 ) -> Option<Self::NameType> {
700 self.perf_counters
701 .get(FnName::rename_net)
702 .measure(|| self.chip.rename_net(net_id, new_name))
703 }
704
705 fn remove_net(&mut self, net: &Self::NetId) {
706 self.perf_counters
707 .get(FnName::remove_net)
708 .measure(|| self.chip.remove_net(net))
709 }
710
711 fn connect_pin(&mut self, pin: &Self::PinId, net: Option<Self::NetId>) -> Option<Self::NetId> {
712 self.perf_counters
713 .get(FnName::connect_pin)
714 .measure(|| self.chip.connect_pin(pin, net))
715 }
716
717 fn connect_pin_instance(
718 &mut self,
719 pin: &Self::PinInstId,
720 net: Option<Self::NetId>,
721 ) -> Option<Self::NetId> {
722 self.perf_counters
723 .get(FnName::connect_pin_instance)
724 .measure(|| self.chip.connect_pin_instance(pin, net))
725 }
726
727 fn disconnect_pin(&mut self, pin: &Self::PinId) -> Option<Self::NetId> {
728 self.perf_counters
729 .get(FnName::disconnect_pin)
730 .measure(|| self.chip.disconnect_pin(pin))
731 }
732
733 fn disconnect_pin_instance(&mut self, pin_instance: &Self::PinInstId) -> Option<Self::NetId> {
734 self.perf_counters
735 .get(FnName::disconnect_pin_instance)
736 .measure(|| self.chip.disconnect_pin_instance(pin_instance))
737 }
738}
739
740impl<L: L2NBase> L2NBase for DBPerf<L> {
741 fn shapes_of_net(&self, net_id: &Self::NetId) -> Box<dyn Iterator<Item = Self::ShapeId> + '_> {
742 self.perf_counters
743 .get(FnName::shapes_of_net)
744 .measure(|| self.chip.shapes_of_net(net_id))
745 }
746
747 fn shapes_of_pin(&self, pin_id: &Self::PinId) -> Box<dyn Iterator<Item = Self::ShapeId> + '_> {
748 self.perf_counters
749 .get(FnName::shapes_of_pin)
750 .measure(|| self.chip.shapes_of_pin(pin_id))
751 }
752
753 fn get_net_of_shape(&self, shape_id: &Self::ShapeId) -> Option<Self::NetId> {
754 self.perf_counters
755 .get(FnName::get_net_of_shape)
756 .measure(|| self.chip.get_net_of_shape(shape_id))
757 }
758
759 fn get_pin_of_shape(&self, shape_id: &Self::ShapeId) -> Option<Self::PinId> {
760 self.perf_counters
761 .get(FnName::get_pin_of_shape)
762 .measure(|| self.chip.get_pin_of_shape(shape_id))
763 }
764}
765
766impl<L: L2NEdit> L2NEdit for DBPerf<L> {
767 fn set_pin_of_shape(
768 &mut self,
769 shape_id: &Self::ShapeId,
770 pin: Option<Self::PinId>,
771 ) -> Option<Self::PinId> {
772 self.perf_counters
773 .get(FnName::set_pin_of_shape)
774 .measure(|| self.chip.set_pin_of_shape(shape_id, pin))
775 }
776
777 fn set_net_of_shape(
778 &mut self,
779 shape_id: &Self::ShapeId,
780 net: Option<Self::NetId>,
781 ) -> Option<Self::NetId> {
782 self.perf_counters
783 .get(FnName::set_net_of_shape)
784 .measure(|| self.chip.set_net_of_shape(shape_id, net))
785 }
786}
787
788#[test]
789fn test_dbperf() {
790 use crate::chip::Chip;
791 let mut chip = Chip::new();
792 let mut chip_perf = DBPerf::new(&mut chip);
793
794 let _cell = chip_perf.create_cell("A".into());
795 let _cell = chip_perf.create_cell("B".into());
796 let _cell = chip_perf.create_cell("C".into());
797
798 let create_cell_perf = chip_perf.get_stats(FnName::create_cell);
800 dbg!(create_cell_perf);
801
802 assert_eq!(create_cell_perf.num_calls, 3);
803 assert!(!create_cell_perf.total_time.is_zero());
804 assert!(create_cell_perf.min_time <= create_cell_perf.max_time);
805 assert!(create_cell_perf.min_time <= create_cell_perf.avg_time().unwrap());
806 assert!(create_cell_perf.avg_time().unwrap() <= create_cell_perf.max_time);
807}