|
| 1 | +import cProfile |
| 2 | +import contextlib |
| 3 | +import time |
| 4 | + |
| 5 | +from parser.astgen.ast_node import AstProgramNode |
| 6 | +from parser.astgen.astgen import AstGen |
| 7 | +from parser.common.tree_print import tformat |
| 8 | +from parser.cst.nodes import ProgramNode |
| 9 | +from parser.cst.treegen import TreeGen |
| 10 | +from parser.lexer import Tokenizer, format_tokens |
| 11 | +from util import readfile |
| 12 | + |
| 13 | +PROFILER = True |
| 14 | + |
| 15 | + |
| 16 | +class _Timer: |
| 17 | + _start = _end = None |
| 18 | + |
| 19 | + def __enter__(self): |
| 20 | + self._start = time.perf_counter() |
| 21 | + return self |
| 22 | + |
| 23 | + def __exit__(self, exc_type, exc_val, exc_tb): |
| 24 | + self._end = time.perf_counter() |
| 25 | + |
| 26 | + def get(self): |
| 27 | + return self._end - self._start |
| 28 | + |
| 29 | + |
| 30 | +class BenchOnce: |
| 31 | + _tokenizer: Tokenizer |
| 32 | + _cstgen: TreeGen |
| 33 | + _cst: ProgramNode |
| 34 | + _ast: AstProgramNode |
| 35 | + |
| 36 | + def __init__(self, src: str, idx: int = -1, do_ast=True): |
| 37 | + self.src = src |
| 38 | + self.idx = idx |
| 39 | + self.should_do_ast = do_ast |
| 40 | + self.lines: list[tuple[float, str]] = [] # First item used as key |
| 41 | + |
| 42 | + @classmethod |
| 43 | + def _fmt_time_taken(cls, name: str, delta_sec: float): |
| 44 | + return f'{name:<17} done in {delta_sec * 1000:.2f}ms' |
| 45 | + |
| 46 | + @classmethod |
| 47 | + def _maybe_profiler(cls): |
| 48 | + if PROFILER: |
| 49 | + return cProfile.Profile() |
| 50 | + return contextlib.nullcontext(None) |
| 51 | + |
| 52 | + def run(self): |
| 53 | + with self._maybe_profiler() as p: |
| 54 | + self.do_tokenize() |
| 55 | + self.do_token_fmt() |
| 56 | + self.do_cst() |
| 57 | + self.do_cst_fmt() |
| 58 | + if self.should_do_ast: |
| 59 | + self.do_ast() |
| 60 | + self.do_ast_fmt() |
| 61 | + if p: |
| 62 | + p.dump_stats(f'perf_dump_{self.idx}.prof') |
| 63 | + print(f'Perf for idx={self.idx} ({PROFILER=}):') |
| 64 | + for _k, s in sorted(self.lines): |
| 65 | + print(f' {s}') |
| 66 | + |
| 67 | + def _add_line(self, sort_key: float, name: str, delta_sec: float): |
| 68 | + self.lines.append((sort_key, self._fmt_time_taken(name, delta_sec))) |
| 69 | + |
| 70 | + def do_tokenize(self): |
| 71 | + with _Timer() as t: |
| 72 | + self._tokenizer = Tokenizer(self.src).tokenize() |
| 73 | + self._add_line(0.0, 'Tokens', t.get()) |
| 74 | + |
| 75 | + def do_token_fmt(self): |
| 76 | + with _Timer() as t: |
| 77 | + _s = format_tokens(self.src, self._tokenizer.tokens, True) |
| 78 | + self._add_line(0.5, 'Tokens_fmt', t.get()) |
| 79 | + |
| 80 | + def do_cst(self): |
| 81 | + with _Timer() as t: |
| 82 | + self._cstgen = TreeGen(self._tokenizer) |
| 83 | + self._cst = self._cstgen.parse() |
| 84 | + self._add_line(1.0, 'CST', t.get()) |
| 85 | + |
| 86 | + def do_cst_fmt(self): |
| 87 | + with _Timer() as t: |
| 88 | + _s = tformat(self._cst) |
| 89 | + self._add_line(1.5, 'CST_fmt', t.get()) |
| 90 | + |
| 91 | + def do_ast(self): |
| 92 | + with _Timer() as t: |
| 93 | + self._ast = AstGen(self._cstgen).parse() |
| 94 | + self._add_line(2.0, 'AST', t.get()) |
| 95 | + |
| 96 | + def do_ast_fmt(self): |
| 97 | + with _Timer() as t: |
| 98 | + _s = tformat(self._ast) |
| 99 | + self._add_line(2.5, 'AST_fmt', t.get()) |
| 100 | + |
| 101 | + |
| 102 | +def benchmark(src: str, idx: int = -1, do_ast=True): |
| 103 | + return BenchOnce(src, idx, do_ast).run() |
| 104 | + |
| 105 | + |
| 106 | +def main(): |
| 107 | + benchmark(readfile('main_example_0.st'), 0, do_ast=False) |
| 108 | + benchmark(readfile('main_example_1.st'), 1) |
| 109 | + |
| 110 | + |
| 111 | +if __name__ == '__main__': |
| 112 | + main() |
0 commit comments