|
102 | 102 | "enable_tracker", |
103 | 103 | "disable_tracker", |
104 | 104 | "Tracker", |
105 | | - "RssTracker", |
| 105 | + "TimeTracker", |
106 | 106 | "PsrecordTracker", |
107 | 107 | "PsrecordMaxrssTracker", |
108 | 108 | "RssPercentilesTracker", |
109 | | - "RssPercentilesAndMaxTracker", |
| 109 | + "RssPercentilesAndTimeTracker", |
110 | 110 | "EnergyConsumptionTracker", |
| 111 | + "get_tracker_class", |
111 | 112 | "BenchmarkExecutor", |
112 | 113 | "make_hwloc_bind", |
113 | 114 | "init_benchmark_suites", |
@@ -766,14 +767,36 @@ def register_command_mapper_hook(self, name: str, hook: Callable | MapperHook): |
766 | 767 | else: |
767 | 768 | raise ValueError(f"Hook must be MapperHook or callable, got {type(hook)}") |
768 | 769 |
|
769 | | - |
770 | 770 | def register_tracker(self, name, tracker_type): |
771 | 771 | tracker = tracker_type(self) |
772 | 772 | self._tracker = tracker |
773 | 773 | hook = tracker.get_hook() |
774 | 774 |
|
775 | 775 | self.register_command_mapper_hook(name, hook) |
776 | 776 |
|
| 777 | + def unregister_tracker(self): |
| 778 | + if self._tracker is None: |
| 779 | + mx.warn("No tracker is currently registered to unregister") |
| 780 | + return |
| 781 | + |
| 782 | + tracker_type = type(self._tracker) |
| 783 | + hook_name_to_remove = None |
| 784 | + |
| 785 | + for name, registered_type in _available_trackers.items(): |
| 786 | + if registered_type == tracker_type: |
| 787 | + hook_name_to_remove = name |
| 788 | + break |
| 789 | + |
| 790 | + if hook_name_to_remove and hook_name_to_remove in self._command_mapper_hooks: |
| 791 | + del self._command_mapper_hooks[hook_name_to_remove] |
| 792 | + else: |
| 793 | + mx.warn("Could not find tracker's hook in registered command mapper hooks") |
| 794 | + |
| 795 | + self._tracker = None |
| 796 | + |
| 797 | + def tracker(self): |
| 798 | + return self._tracker |
| 799 | + |
777 | 800 | def version(self): |
778 | 801 | """The suite version selected for execution which is either the :defaultSuiteVerion: |
779 | 802 | or the :desiredVersion: if any. |
@@ -3575,77 +3598,61 @@ def get_rules(self, bmSuiteArgs): |
3575 | 3598 | def get_hook(self) -> MapperHook: |
3576 | 3599 | return DefaultTrackerHook(self) |
3577 | 3600 |
|
3578 | | -class RssTracker(Tracker): |
| 3601 | +class TimeTracker(Tracker): |
3579 | 3602 | def __init__(self, bmSuite): |
3580 | 3603 | super().__init__(bmSuite) |
3581 | | - log_deprecation("The 'rss' tracker is deprecated, use 'rsspercentiles' instead!") |
| 3604 | + self._timing_wrapper = None |
| 3605 | + |
| 3606 | + if mx.get_os() != "linux": |
| 3607 | + script_dir = os.path.dirname(os.path.abspath(__file__)) |
| 3608 | + self._timing_wrapper = os.path.join(script_dir, "timing_wrapper.py") |
| 3609 | + if not os.path.exists(self._timing_wrapper): |
| 3610 | + mx.warn(f"Timing wrapper not found at {self._timing_wrapper}") |
| 3611 | + self._timing_wrapper = None |
3582 | 3612 |
|
3583 | 3613 | def map_command(self, cmd): |
3584 | 3614 | """ |
3585 | | - Tracks the max resident memory size used by the process using the 'time' command. |
| 3615 | + Tracks wall-clock time using platform-appropriate timing commands. |
3586 | 3616 |
|
3587 | 3617 | :param list[str] cmd: the input command to modify |
3588 | | - :return: |
| 3618 | + :return: list[str] modified command with timing prefix |
3589 | 3619 | """ |
3590 | 3620 | if not _use_tracker: |
3591 | 3621 | return cmd |
| 3622 | + |
3592 | 3623 | if mx.get_os() == "linux": |
3593 | | - prefix = ["time", "-v"] |
3594 | | - elif mx.get_os() == "darwin": |
3595 | | - prefix = ["time", "-l"] |
| 3624 | + # Forces GNU time with '/usr/bin/time' instead of shell builtin with 'time' |
| 3625 | + prefix = ["/usr/bin/time", "-f", "Wall-clock time: %e sec"] |
3596 | 3626 | else: |
3597 | | - mx.log(f"Ignoring the 'rss' tracker since it is not supported on {mx.get_os()}") |
3598 | | - prefix = [] |
| 3627 | + if self._timing_wrapper and os.path.exists(self._timing_wrapper): |
| 3628 | + prefix = [self._timing_wrapper] |
| 3629 | + else: |
| 3630 | + raise ValueError(f"Timing wrapper not found at {self._timing_wrapper}") |
| 3631 | + |
3599 | 3632 | return prefix + cmd |
3600 | 3633 |
|
3601 | 3634 | def get_rules(self, bmSuiteArgs): |
3602 | 3635 | if mx_benchmark_compatibility().bench_suite_needs_suite_args(): |
3603 | 3636 | suite_name = self.bmSuite.benchSuiteName(bmSuiteArgs) |
3604 | 3637 | else: |
3605 | 3638 | suite_name = self.bmSuite.benchSuiteName() |
3606 | | - if mx.get_os() == "linux": |
3607 | | - # Output of 'time -v' on linux contains: |
3608 | | - # Maximum resident set size (kbytes): 511336 |
3609 | | - rules = [ |
3610 | | - StdOutRule( |
3611 | | - r"Maximum resident set size \(kbytes\): (?P<rss>[0-9]+)", |
3612 | | - { |
3613 | | - "benchmark": self.bmSuite.currently_running_benchmark(), |
3614 | | - "bench-suite": suite_name, |
3615 | | - "config.vm-flags": ' '.join(self.bmSuite.vmArgs(bmSuiteArgs)), |
3616 | | - "metric.name": "max-rss", |
3617 | | - "metric.value": ("<rss>", lambda x: int(float(x)/(1024))), |
3618 | | - "metric.unit": "MB", |
3619 | | - "metric.type": "numeric", |
3620 | | - "metric.score-function": "id", |
3621 | | - "metric.better": "lower", |
3622 | | - "metric.iteration": 0 |
3623 | | - } |
3624 | | - ) |
3625 | | - ] |
3626 | | - elif mx.get_os() == "darwin": |
3627 | | - # Output of 'time -l' on linux contains (size in bytes): |
3628 | | - # 523608064 maximum resident set size |
3629 | | - rules = [ |
3630 | | - StdOutRule( |
3631 | | - r"(?P<rss>[0-9]+)\s+maximum resident set size", |
3632 | | - { |
3633 | | - "benchmark": self.bmSuite.currently_running_benchmark(), |
3634 | | - "bench-suite": suite_name, |
3635 | | - "config.vm-flags": ' '.join(self.bmSuite.vmArgs(bmSuiteArgs)), |
3636 | | - "metric.name": "max-rss", |
3637 | | - "metric.value": ("<rss>", lambda x: int(float(x)/(1024*1024))), |
3638 | | - "metric.unit": "MB", |
3639 | | - "metric.type": "numeric", |
3640 | | - "metric.score-function": "id", |
3641 | | - "metric.better": "lower", |
3642 | | - "metric.iteration": 0 |
3643 | | - } |
3644 | | - ) |
3645 | | - ] |
3646 | | - else: |
3647 | | - rules = [] |
3648 | | - return rules |
| 3639 | + return [ |
| 3640 | + StdOutRule( |
| 3641 | + r"Wall-clock time:\s+(?P<time_sec>[0-9]+\.[0-9]+) sec", |
| 3642 | + { |
| 3643 | + "benchmark": self.bmSuite.currently_running_benchmark(), |
| 3644 | + "bench-suite": suite_name, |
| 3645 | + "config.vm-flags": ' '.join(self.bmSuite.vmArgs(bmSuiteArgs)), |
| 3646 | + "metric.name": "time", |
| 3647 | + "metric.unit": "ms", |
| 3648 | + "metric.value": ("<time_sec>", lambda ms: float(ms) * 1000), |
| 3649 | + "metric.type": "numeric", |
| 3650 | + "metric.score-function": "id", |
| 3651 | + "metric.better": "lower", |
| 3652 | + "metric.iteration": 0 |
| 3653 | + } |
| 3654 | + ) |
| 3655 | + ] |
3649 | 3656 |
|
3650 | 3657 |
|
3651 | 3658 | class PsrecordTracker(Tracker): |
@@ -3727,7 +3734,7 @@ class PsrecordMaxrssTracker(Tracker): |
3727 | 3734 |
|
3728 | 3735 | def __init__(self, bmSuite): |
3729 | 3736 | super().__init__(bmSuite) |
3730 | | - self.rss = RssTracker(bmSuite) |
| 3737 | + self.rss = TimeTracker(bmSuite) |
3731 | 3738 | self.psrecord = PsrecordTracker(bmSuite) |
3732 | 3739 |
|
3733 | 3740 | def map_command(self, cmd): |
@@ -3911,22 +3918,21 @@ def parseResults(self, text): |
3911 | 3918 | return [] |
3912 | 3919 |
|
3913 | 3920 |
|
3914 | | -class RssPercentilesAndMaxTracker(Tracker): |
| 3921 | +class RssPercentilesAndTimeTracker(Tracker): |
3915 | 3922 | def __init__(self, bmSuite): |
3916 | 3923 | super().__init__(bmSuite) |
3917 | | - self.rss_max_tracker = RssTracker(bmSuite) |
| 3924 | + self.time_tracker = TimeTracker(bmSuite) |
3918 | 3925 | self.rss_percentiles_tracker = RssPercentilesTracker( |
3919 | 3926 | bmSuite, |
3920 | 3927 | skip=1, # skip RSS of the 'time' command |
3921 | | - copy_into_max_rss=False # don't copy a percentile into max-rss, since the rss_max_tracker will generate the metric |
| 3928 | + copy_into_max_rss=True |
3922 | 3929 | ) |
3923 | | - log_deprecation("The 'rsspercentiles+maxrss' tracker is deprecated, use 'rsspercentiles' instead!") |
3924 | 3930 |
|
3925 | 3931 | def map_command(self, cmd): |
3926 | | - return self.rss_percentiles_tracker.map_command(self.rss_max_tracker.map_command(cmd)) |
| 3932 | + return self.rss_percentiles_tracker.map_command(self.time_tracker.map_command(cmd)) |
3927 | 3933 |
|
3928 | 3934 | def get_rules(self, bmSuiteArgs): |
3929 | | - return self.rss_max_tracker.get_rules(bmSuiteArgs) + self.rss_percentiles_tracker.get_rules(bmSuiteArgs) |
| 3935 | + return self.time_tracker.get_rules(bmSuiteArgs) + self.rss_percentiles_tracker.get_rules(bmSuiteArgs) |
3930 | 3936 |
|
3931 | 3937 | class EnergyConsumptionTracker(Tracker): |
3932 | 3938 | """ |
@@ -4060,14 +4066,19 @@ def should_apply(self, stage: Optional[Stage]) -> bool: |
4060 | 4066 | return stage.is_final() if stage else False |
4061 | 4067 |
|
4062 | 4068 | _available_trackers = { |
4063 | | - "rss": RssTracker, |
| 4069 | + "time": TimeTracker, |
4064 | 4070 | "psrecord": PsrecordTracker, |
4065 | 4071 | "psrecord+maxrss": PsrecordMaxrssTracker, |
4066 | 4072 | "rsspercentiles": RssPercentilesTracker, |
4067 | | - "rsspercentiles+maxrss": RssPercentilesAndMaxTracker, |
| 4073 | + "rsspercentiles+time": RssPercentilesAndTimeTracker, |
4068 | 4074 | "energy": EnergyConsumptionTracker |
4069 | 4075 | } |
4070 | 4076 |
|
| 4077 | +def get_tracker_class(tracker_name): |
| 4078 | + if tracker_name not in _available_trackers: |
| 4079 | + raise ValueError(f"Tracker '{tracker_name}' doesn't exist! Use one of: [{', '.join(_available_trackers.keys())}]") |
| 4080 | + return _available_trackers[tracker_name] |
| 4081 | + |
4071 | 4082 |
|
4072 | 4083 | class BenchmarkExecutor(object): |
4073 | 4084 | def uid(self): |
|
0 commit comments