Skip to content

Commit 62d8b26

Browse files
authored
Merge pull request #2651 from Robbybp/htimer-utils
Add utility methods to HierarchicalTimer
2 parents 497336a + 2b4c569 commit 62d8b26

File tree

2 files changed

+486
-2
lines changed

2 files changed

+486
-2
lines changed

pyomo/common/tests/test_timing.py

+258
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,261 @@ def test_HierarchicalTimer_longNames(self):
393393
'='*79)).splitlines()
394394
for l, r in zip(str(timer).splitlines(), ref):
395395
self.assertRegex(l, r)
396+
397+
def test_clear_except_base_timer(self):
398+
timer = HierarchicalTimer()
399+
timer.start("a")
400+
timer.start("b")
401+
timer.stop("b")
402+
timer.stop("a")
403+
timer.start("c")
404+
timer.stop("c")
405+
timer.start("d")
406+
timer.stop("d")
407+
timer.clear_except("b", "c")
408+
key_set = set(timer.timers.keys())
409+
self.assertEqual(key_set, {"c"})
410+
411+
def test_clear_except_subtimer(self):
412+
# Testing this method on "sub-timers" exercises different code
413+
# as while the base timer is a HierarchicalTimer, the sub-timers
414+
# are _HierarchicalHelpers
415+
timer = HierarchicalTimer()
416+
timer.start("root")
417+
timer.start("a")
418+
timer.start("b")
419+
timer.stop("b")
420+
timer.stop("a")
421+
timer.start("c")
422+
timer.stop("c")
423+
timer.start("d")
424+
timer.stop("d")
425+
timer.stop("root")
426+
root = timer.timers["root"]
427+
root.clear_except("b", "c")
428+
key_set = set(root.timers.keys())
429+
self.assertEqual(key_set, {"c"})
430+
431+
432+
class TestFlattenHierarchicalTimer(unittest.TestCase):
433+
434+
#
435+
# The following methods create some hierarchical timers, then
436+
# hand-code the total time of each timer in the data structure.
437+
# This is so we can assert that total_time fields of flattened
438+
# timers are computed correctly without relying on the actual
439+
# time spent.
440+
#
441+
def make_singleton_timer(self):
442+
timer = HierarchicalTimer()
443+
timer.start("root")
444+
timer.stop("root")
445+
timer.timers["root"].total_time = 5.0
446+
return timer
447+
448+
def make_flat_timer(self):
449+
timer = HierarchicalTimer()
450+
timer.start("root")
451+
timer.start("a")
452+
timer.stop("a")
453+
timer.start("b")
454+
timer.stop("b")
455+
timer.stop("root")
456+
timer.timers["root"].total_time = 5.0
457+
timer.timers["root"].timers["a"].total_time = 1.0
458+
timer.timers["root"].timers["b"].total_time = 2.5
459+
return timer
460+
461+
def make_timer_depth_2_one_child(self):
462+
timer = HierarchicalTimer()
463+
timer.start("root")
464+
timer.start("a")
465+
timer.start("b")
466+
timer.stop("b")
467+
timer.start("c")
468+
timer.stop("c")
469+
timer.stop("a")
470+
timer.stop("root")
471+
timer.timers["root"].total_time = 5.0
472+
timer.timers["root"].timers["a"].total_time = 4.0
473+
timer.timers["root"].timers["a"].timers["b"].total_time = 1.1
474+
timer.timers["root"].timers["a"].timers["c"].total_time = 2.2
475+
return timer
476+
477+
def make_timer_depth_2_with_name_collision(self):
478+
timer = HierarchicalTimer()
479+
timer.start("root")
480+
timer.start("a")
481+
timer.start("b")
482+
timer.stop("b")
483+
timer.start("c")
484+
timer.stop("c")
485+
timer.stop("a")
486+
timer.start("b")
487+
timer.stop("b")
488+
timer.stop("root")
489+
timer.timers["root"].total_time = 5.0
490+
timer.timers["root"].timers["a"].total_time = 4.0
491+
timer.timers["root"].timers["a"].timers["b"].total_time = 1.1
492+
timer.timers["root"].timers["a"].timers["c"].total_time = 2.2
493+
timer.timers["root"].timers["b"].total_time = 0.11
494+
return timer
495+
496+
def make_timer_depth_2_two_children(self):
497+
timer = HierarchicalTimer()
498+
timer.start("root")
499+
timer.start("a")
500+
timer.start("b")
501+
timer.stop("b")
502+
timer.start("c")
503+
timer.stop("c")
504+
timer.stop("a")
505+
timer.start("b")
506+
timer.start("c")
507+
timer.stop("c")
508+
timer.start("d")
509+
timer.stop("d")
510+
timer.stop("b")
511+
timer.stop("root")
512+
timer.timers["root"].total_time = 5.0
513+
timer.timers["root"].timers["a"].total_time = 4.0
514+
timer.timers["root"].timers["a"].timers["b"].total_time = 1.1
515+
timer.timers["root"].timers["a"].timers["c"].total_time = 2.2
516+
timer.timers["root"].timers["b"].total_time = 0.88
517+
timer.timers["root"].timers["b"].timers["c"].total_time = 0.07
518+
timer.timers["root"].timers["b"].timers["d"].total_time = 0.05
519+
return timer
520+
521+
def make_timer_depth_4(self):
522+
timer = HierarchicalTimer()
523+
timer.start("root")
524+
timer.start("a")
525+
timer.start("b")
526+
timer.stop("b")
527+
timer.start("c")
528+
timer.start("d")
529+
timer.start("e")
530+
timer.stop("e")
531+
timer.stop("d")
532+
timer.stop("c")
533+
timer.stop("a")
534+
timer.start("b")
535+
timer.start("c")
536+
timer.start("e")
537+
timer.stop("e")
538+
timer.stop("c")
539+
timer.start("d")
540+
timer.stop("d")
541+
timer.stop("b")
542+
timer.stop("root")
543+
timer.timers["root"].total_time = 5.0
544+
timer.timers["root"].timers["a"].total_time = 4.0
545+
timer.timers["root"].timers["a"].timers["b"].total_time = 1.1
546+
timer.timers["root"].timers["a"].timers["c"].total_time = 2.2
547+
timer.timers["root"].timers["a"].timers["c"].timers["d"].total_time = 0.9
548+
timer.timers["root"].timers["a"].timers["c"].timers["d"].timers["e"].total_time = 0.6
549+
timer.timers["root"].timers["b"].total_time = 0.88
550+
timer.timers["root"].timers["b"].timers["c"].total_time = 0.07
551+
timer.timers["root"].timers["b"].timers["c"].timers["e"].total_time = 0.04
552+
timer.timers["root"].timers["b"].timers["d"].total_time = 0.05
553+
return timer
554+
555+
def make_timer_depth_4_same_name(self):
556+
timer = HierarchicalTimer()
557+
timer.start("root")
558+
timer.start("a")
559+
timer.start("a")
560+
timer.start("a")
561+
timer.start("a")
562+
timer.stop("a")
563+
timer.stop("a")
564+
timer.stop("a")
565+
timer.stop("a")
566+
timer.stop("root")
567+
timer.timers["root"].total_time = 5.0
568+
timer.timers["root"].timers["a"].total_time = 1.0
569+
timer.timers["root"].timers["a"].timers["a"].total_time = 0.1
570+
timer.timers["root"].timers["a"].timers["a"].timers["a"].total_time = 0.01
571+
timer.timers["root"].timers["a"].timers["a"].timers["a"].timers["a"].total_time = 0.001
572+
return timer
573+
574+
def test_singleton(self):
575+
timer = self.make_singleton_timer()
576+
root = timer.timers["root"]
577+
root.flatten()
578+
self.assertAlmostEqual(root.total_time, 5.0)
579+
580+
def test_already_flat(self):
581+
timer = self.make_flat_timer()
582+
root = timer.timers["root"]
583+
root.flatten()
584+
self.assertAlmostEqual(root.total_time, 5.0)
585+
self.assertAlmostEqual(root.timers["a"].total_time, 1.0)
586+
self.assertAlmostEqual(root.timers["b"].total_time, 2.5)
587+
588+
def test_depth_2_one_child(self):
589+
timer = self.make_timer_depth_2_one_child()
590+
root = timer.timers["root"]
591+
root.flatten()
592+
self.assertAlmostEqual(root.total_time, 5.0)
593+
self.assertAlmostEqual(root.timers["a"].total_time, 0.7)
594+
self.assertAlmostEqual(root.timers["b"].total_time, 1.1)
595+
self.assertAlmostEqual(root.timers["c"].total_time, 2.2)
596+
597+
def test_timer_depth_2_with_name_collision(self):
598+
timer = self.make_timer_depth_2_with_name_collision()
599+
root = timer.timers["root"]
600+
root.flatten()
601+
self.assertAlmostEqual(root.total_time, 5.0)
602+
self.assertAlmostEqual(root.timers["a"].total_time, 0.700)
603+
self.assertAlmostEqual(root.timers["b"].total_time, 1.210)
604+
self.assertAlmostEqual(root.timers["c"].total_time, 2.200)
605+
606+
def test_timer_depth_2_two_children(self):
607+
timer = self.make_timer_depth_2_two_children()
608+
root = timer.timers["root"]
609+
root.flatten()
610+
self.assertAlmostEqual(root.total_time, 5.0)
611+
self.assertAlmostEqual(root.timers["a"].total_time, 0.700)
612+
self.assertAlmostEqual(root.timers["b"].total_time, 1.860)
613+
self.assertAlmostEqual(root.timers["c"].total_time, 2.270)
614+
self.assertAlmostEqual(root.timers["d"].total_time, 0.050)
615+
616+
def test_timer_depth_4(self):
617+
timer = self.make_timer_depth_4()
618+
root = timer.timers["root"]
619+
root.flatten()
620+
self.assertAlmostEqual(root.total_time, 5.0)
621+
self.assertAlmostEqual(root.timers["a"].total_time, 0.700)
622+
self.assertAlmostEqual(root.timers["b"].total_time, 1.860)
623+
self.assertAlmostEqual(root.timers["c"].total_time, 1.330)
624+
self.assertAlmostEqual(root.timers["d"].total_time, 0.350)
625+
self.assertAlmostEqual(root.timers["e"].total_time, 0.640)
626+
627+
def test_timer_depth_4_same_name(self):
628+
timer = self.make_timer_depth_4_same_name()
629+
root = timer.timers["root"]
630+
root.flatten()
631+
self.assertAlmostEqual(root.total_time, 5.0)
632+
self.assertAlmostEqual(root.timers["a"].total_time, 1.0)
633+
634+
def test_base_timer_depth_3(self):
635+
# This is depth 2 wrt the root, depth 3 wrt the
636+
# "base timer"
637+
timer = self.make_timer_depth_2_two_children()
638+
timer.flatten()
639+
self.assertAlmostEqual(timer.timers["root"].total_time, 0.120)
640+
self.assertAlmostEqual(timer.timers["a"].total_time, 0.700)
641+
self.assertAlmostEqual(timer.timers["b"].total_time, 1.860)
642+
self.assertAlmostEqual(timer.timers["c"].total_time, 2.270)
643+
self.assertAlmostEqual(timer.timers["d"].total_time, 0.050)
644+
645+
def test_timer_still_active(self):
646+
timer = HierarchicalTimer()
647+
timer.start("a")
648+
timer.stop("a")
649+
timer.start("b")
650+
msg = "Cannot flatten.*while any timers are active"
651+
with self.assertRaisesRegex(RuntimeError, msg):
652+
timer.flatten()
653+
timer.stop("b")

0 commit comments

Comments
 (0)