| # Copyright 2020 Google Inc. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """Python benchmarking utilities. | |
| Example usage: | |
| import google_benchmark as benchmark | |
| @benchmark.register | |
| def my_benchmark(state): | |
| ... # Code executed outside `while` loop is not timed. | |
| while state: | |
| ... # Code executed within `while` loop is timed. | |
| if __name__ == '__main__': | |
| benchmark.main() | |
| """ | |
| import atexit | |
| from absl import app | |
| from google_benchmark import _benchmark | |
| from google_benchmark._benchmark import ( | |
| Counter, | |
| kNanosecond, | |
| kMicrosecond, | |
| kMillisecond, | |
| kSecond, | |
| oNone, | |
| o1, | |
| oN, | |
| oNSquared, | |
| oNCubed, | |
| oLogN, | |
| oNLogN, | |
| oAuto, | |
| oLambda, | |
| State, | |
| ) | |
| __all__ = [ | |
| "register", | |
| "main", | |
| "Counter", | |
| "kNanosecond", | |
| "kMicrosecond", | |
| "kMillisecond", | |
| "kSecond", | |
| "oNone", | |
| "o1", | |
| "oN", | |
| "oNSquared", | |
| "oNCubed", | |
| "oLogN", | |
| "oNLogN", | |
| "oAuto", | |
| "oLambda", | |
| "State", | |
| ] | |
| __version__ = "1.7.1" | |
| class __OptionMaker: | |
| """A stateless class to collect benchmark options. | |
| Collect all decorator calls like @option.range(start=0, limit=1<<5). | |
| """ | |
| class Options: | |
| """Pure data class to store options calls, along with the benchmarked function.""" | |
| def __init__(self, func): | |
| self.func = func | |
| self.builder_calls = [] | |
| @classmethod | |
| def make(cls, func_or_options): | |
| """Make Options from Options or the benchmarked function.""" | |
| if isinstance(func_or_options, cls.Options): | |
| return func_or_options | |
| return cls.Options(func_or_options) | |
| def __getattr__(self, builder_name): | |
| """Append option call in the Options.""" | |
| # The function that get returned on @option.range(start=0, limit=1<<5). | |
| def __builder_method(*args, **kwargs): | |
| # The decorator that get called, either with the benchmared function | |
| # or the previous Options | |
| def __decorator(func_or_options): | |
| options = self.make(func_or_options) | |
| options.builder_calls.append((builder_name, args, kwargs)) | |
| # The decorator returns Options so it is not technically a decorator | |
| # and needs a final call to @register | |
| return options | |
| return __decorator | |
| return __builder_method | |
| # Alias for nicer API. | |
| # We have to instantiate an object, even if stateless, to be able to use __getattr__ | |
| # on option.range | |
| option = __OptionMaker() | |
| def register(undefined=None, *, name=None): | |
| """Register function for benchmarking.""" | |
| if undefined is None: | |
| # Decorator is called without parenthesis so we return a decorator | |
| return lambda f: register(f, name=name) | |
| # We have either the function to benchmark (simple case) or an instance of Options | |
| # (@option._ case). | |
| options = __OptionMaker.make(undefined) | |
| if name is None: | |
| name = options.func.__name__ | |
| # We register the benchmark and reproduce all the @option._ calls onto the | |
| # benchmark builder pattern | |
| benchmark = _benchmark.RegisterBenchmark(name, options.func) | |
| for name, args, kwargs in options.builder_calls[::-1]: | |
| getattr(benchmark, name)(*args, **kwargs) | |
| # return the benchmarked function because the decorator does not modify it | |
| return options.func | |
| def _flags_parser(argv): | |
| argv = _benchmark.Initialize(argv) | |
| return app.parse_flags_with_usage(argv) | |
| def _run_benchmarks(argv): | |
| if len(argv) > 1: | |
| raise app.UsageError("Too many command-line arguments.") | |
| return _benchmark.RunSpecifiedBenchmarks() | |
| def main(argv=None): | |
| return app.run(_run_benchmarks, argv=argv, flags_parser=_flags_parser) | |
| # Methods for use with custom main function. | |
| initialize = _benchmark.Initialize | |
| run_benchmarks = _benchmark.RunSpecifiedBenchmarks | |
| atexit.register(_benchmark.ClearRegisteredBenchmarks) |