| #!/usr/bin/env python3 |
| |
| import argparse |
| import ast |
| import sys |
| import os |
| from time import time |
| |
| import _peg_parser |
| |
| try: |
| import memory_profiler |
| except ModuleNotFoundError: |
| print("Please run `make venv` to create a virtual environment and install" |
| " all the dependencies, before running this script.") |
| sys.exit(1) |
| |
| sys.path.insert(0, os.getcwd()) |
| from scripts.test_parse_directory import parse_directory |
| |
| argparser = argparse.ArgumentParser( |
| prog="benchmark", description="Reproduce the various pegen benchmarks" |
| ) |
| argparser.add_argument( |
| "--parser", |
| action="store", |
| choices=["new", "old"], |
| default="pegen", |
| help="Which parser to benchmark (default is pegen)", |
| ) |
| argparser.add_argument( |
| "--target", |
| action="store", |
| choices=["xxl", "stdlib"], |
| default="xxl", |
| help="Which target to use for the benchmark (default is xxl.py)", |
| ) |
| |
| subcommands = argparser.add_subparsers(title="Benchmarks", dest="subcommand") |
| command_compile = subcommands.add_parser( |
| "compile", help="Benchmark parsing and compiling to bytecode" |
| ) |
| command_parse = subcommands.add_parser( |
| "parse", help="Benchmark parsing and generating an ast.AST" |
| ) |
| command_notree = subcommands.add_parser( |
| "notree", help="Benchmark parsing and dumping the tree" |
| ) |
| |
| |
| def benchmark(func): |
| def wrapper(*args): |
| times = list() |
| for _ in range(3): |
| start = time() |
| result = func(*args) |
| end = time() |
| times.append(end - start) |
| memory = memory_profiler.memory_usage((func, args)) |
| print(f"{func.__name__}") |
| print(f"\tTime: {sum(times)/3:.3f} seconds on an average of 3 runs") |
| print(f"\tMemory: {max(memory)} MiB on an average of 3 runs") |
| return result |
| |
| return wrapper |
| |
| |
| @benchmark |
| def time_compile(source, parser): |
| if parser == "old": |
| return _peg_parser.compile_string( |
| source, |
| oldparser=True, |
| ) |
| else: |
| return _peg_parser.compile_string(source) |
| |
| |
| @benchmark |
| def time_parse(source, parser): |
| if parser == "old": |
| return _peg_parser.parse_string(source, oldparser=True) |
| else: |
| return _peg_parser.parse_string(source) |
| |
| |
| @benchmark |
| def time_notree(source, parser): |
| if parser == "old": |
| return _peg_parser.parse_string(source, oldparser=True, ast=False) |
| else: |
| return _peg_parser.parse_string(source, ast=False) |
| |
| |
| def run_benchmark_xxl(subcommand, parser, source): |
| if subcommand == "compile": |
| time_compile(source, parser) |
| elif subcommand == "parse": |
| time_parse(source, parser) |
| elif subcommand == "notree": |
| time_notree(source, parser) |
| |
| |
| def run_benchmark_stdlib(subcommand, parser): |
| modes = {"compile": 2, "parse": 1, "notree": 0} |
| for _ in range(3): |
| parse_directory( |
| "../../Lib", |
| verbose=False, |
| excluded_files=["*/bad*", "*/lib2to3/tests/data/*",], |
| tree_arg=0, |
| short=True, |
| mode=modes[subcommand], |
| oldparser=(parser == "old"), |
| ) |
| |
| |
| def main(): |
| args = argparser.parse_args() |
| subcommand = args.subcommand |
| parser = args.parser |
| target = args.target |
| |
| if subcommand is None: |
| argparser.error("A benchmark to run is required") |
| |
| if target == "xxl": |
| with open(os.path.join("data", "xxl.py"), "r") as f: |
| source = f.read() |
| run_benchmark_xxl(subcommand, parser, source) |
| elif target == "stdlib": |
| run_benchmark_stdlib(subcommand, parser) |
| |
| |
| if __name__ == "__main__": |
| main() |