| # 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.8.0" |
| |
| |
| 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) |