Coverage for mlos_bench/mlos_bench/run.py: 87%
30 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-21 01:50 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-21 01:50 +0000
1#!/usr/bin/env python3
2#
3# Copyright (c) Microsoft Corporation.
4# Licensed under the MIT License.
5#
6"""
7mlos_bench main optimization loop and benchmark runner CLI.
9Note: this script is also available as a CLI tool via ``pip`` under the name ``mlos_bench``.
11See the current ``--help`` `output for details <../../../mlos_bench.run.usage.html>`_.
13See Also
14--------
15mlos_bench.config : documentation on the configuration system.
16mlos_bench.launcher.Launcher : class is responsible for processing the CLI args.
17"""
19import logging
20import sys
22import numpy as np
24from mlos_bench.launcher import Launcher
25from mlos_bench.tunables.tunable_groups import TunableGroups
27_LOG = logging.getLogger(__name__)
30def _sanity_check_results(launcher: Launcher) -> None:
31 """Do some sanity checking on the results and throw an exception if it looks like
32 something went wrong.
33 """
34 basic_err_msg = "Check configuration, scripts, and logs for details."
36 # Check if the scheduler has any trials.
37 if not launcher.scheduler.trial_count:
38 raise RuntimeError(f"No trials were run. {basic_err_msg}")
40 # Check if the scheduler ran the expected number of trials.
41 expected_trial_count = min(
42 launcher.scheduler.max_trials if launcher.scheduler.max_trials > 0 else np.inf,
43 launcher.scheduler.trial_config_repeat_count * launcher.optimizer.max_suggestions,
44 )
45 if launcher.scheduler.trial_count < expected_trial_count:
46 raise RuntimeError(
47 f"Expected {expected_trial_count} trials, "
48 f"but only {launcher.scheduler.trial_count} were run. {basic_err_msg}"
49 )
51 # Check to see if "too many" trials seem to have failed (#523).
52 unsuccessful_trials = [t for t in launcher.scheduler.ran_trials if not t.status.is_succeeded()]
53 if len(unsuccessful_trials) > 0.2 * launcher.scheduler.trial_count:
54 raise RuntimeWarning(
55 "Too many trials failed: "
56 f"{len(unsuccessful_trials)} out of {launcher.scheduler.trial_count}. "
57 f"{basic_err_msg}"
58 )
61def _main(
62 argv: list[str] | None = None,
63) -> tuple[dict[str, float] | None, TunableGroups | None]:
64 launcher = Launcher("mlos_bench", "Systems autotuning and benchmarking tool", argv=argv)
66 with launcher.scheduler as scheduler_context:
67 scheduler_context.start()
68 scheduler_context.teardown()
70 _sanity_check_results(launcher)
72 (score, _config) = result = launcher.scheduler.get_best_observation()
73 # NOTE: This log line is used in test_launch_main_app_* unit tests:
74 _LOG.info("Final score: %s", score)
75 return result
78def _shell_main(
79 argv: list[str] | None = None,
80) -> int:
81 (best_score, best_config) = _main(argv)
82 # Exit zero if it looks like the overall operation was successful.
83 # TODO: Improve this sanity check to be more robust.
84 if (
85 best_score
86 and best_config
87 and all(isinstance(score_value, float) for score_value in best_score.values())
88 ):
89 return 0
90 else:
91 raise ValueError(f"Unexpected result: {best_score=}, {best_config=}")
94if __name__ == "__main__":
95 sys.exit(_shell_main())