# This script is best run with pystats enabled to help visualize the shape of the traces. # ./configure --enable-experimental-jit=interpreter -C --with-pydebug --enable-pystats # The resulting images can be visualize on linux as follows: # $ cd folder_with_gv_files # $ dot -Tsvg -Osvg *.gv # $ firefox *.gv.svg # type: ignore import sys import os.path from types import FunctionType # All functions declared in this module will be run to generate # a .gv file of the executors, unless the name starts with an underscore. def _gen(n): for _ in range(n): yield n def gen_in_loop(n): t = 0 for n in _gen(n): t += n return n def short_loop(n): t = 0 for _ in range(n): t += 1 t += 1 t += 1 t += 1 t += 1 return t exec( "\n".join( ["def mid_loop(n):"] + [" t = 0"] + [" for _ in range(n):"] + [" t += 1"] * 20 + [" return t"] ), globals(), ) exec( "\n".join( ["def long_loop(n):"] + [" t = 0"] + [" for _ in range(n):"] + [" t += 1"] * 100 + [" return t"] ), globals(), ) def _add(a, b): return a + b def short_loop_with_calls(n): t = 0 for _ in range(n): t = _add(t, 1) t = _add(t, 1) t = _add(t, 1) t = _add(t, 1) t = _add(t, 1) return t exec( "\n".join( ["def mid_loop_with_calls(n):"] + [" t = 0"] + [" for _ in range(n):"] + [" t = _add(t, 1)"] * 20 + [" return t"] ), globals(), ) exec( "\n".join( ["def long_loop_with_calls(n):"] + [" t = 0"] + [" for _ in range(n):"] + [" t = _add(t, 1)"] * 100 + [" return t"] ), globals(), ) def short_loop_with_side_exits(n): t = 0 for i in range(n): if t < 0: break t += 1 if t < 0: break t += 1 if t < 0: break t += 1 if t < 0: break t += 1 if t < 0: break t += 1 return t exec( "\n".join( ["def mid_loop_with_side_exits(n):"] + [" t = 0"] + [" for _ in range(n):"] + [" if t < 0:", " break", " t += 1"] * 20 + [" return t"] ), globals(), ) exec( "\n".join( ["def long_loop_with_side_exits(n):"] + [" t = 0"] + [" for _ in range(n):"] + [" if t < 0:", " break", " t += 1"] * 100 + [" return t"] ), globals(), ) def short_branchy_loop(n): # Branches are correlated and exit 1 time in 4. t = 0 for i in range(n): # Start with a few operations to form a viable trace t += 1 t += 1 t += 1 if not t & 6: continue t += 1 if not t & 12: continue t += 1 if not t & 24: continue t += 1 if not t & 48: continue t += 1 return t def _run_and_dump(func, n, outdir): sys._clear_internal_caches() func(n) sys._dump_tracelets(os.path.join(outdir, f"{func.__name__}.gv")) def _main(): if len(sys.argv) < 2 or len(sys.argv) > 3: print(f"Usage: {sys.argv[0] if sys.argv else " "} OUTDIR [loops]") outdir = sys.argv[1] n = int(sys.argv[2]) if len(sys.argv) > 2 else 5000 functions = [ func for func in globals().values() if isinstance(func, FunctionType) and not func.__name__.startswith("_") ] for func in functions: _run_and_dump(func, n, outdir) if __name__ == "__main__": _main()