# Performance - cProfile - pstats ::: notes Se présenter. # Le code ```python def main(): already_checked = [] while True: c = "".join(choice(ascii_letters) for _ in range(10)) if c in already_checked: continue already_checked.append(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) print("Searching") ``` ## Premiers tests ```bash $ time python perf.py AFPy 00 Searching [...] Searching Found: sha512(5NX3dB0BrO + AFPy) = 00… real 0m0.048s user 0m0.040s sys 0m0.008s ``` ## Premiers tests ```bash $ time python perf.py AFPy 000 Searching [...] Searching Found: sha512(UYb0z6nac1 + AFPy) = 000… real 0m2.797s user 0m2.773s sys 0m0.024s ``` ## Premiers tests ```bash $ time python perf.py AFPy 0000 Searching [...] Searching Found: sha512(dX0oAzvOmm + AFPy) = 0000… real 0m16.381s user 0m16.375s sys 0m0.004s ``` C'est long mais ça passe … ## Premiers tests ```bash $ time python perf.py AFPy 00000 Searching [...] Searching Searching Searching Searching ``` Bon, on a un sushi. ## cProfile ```bash $ python -m cProfile -o prof perf.py AFPy 0000 ``` ## pstats ```bash $ python -m pstats prof Welcome to the profile statistics browser. prof% sort cumulative prof% stats 10 ``` ## pstats ```txt ncalls cumtime percall filename:lineno(function) 12/1 17.007 17.007 {built-in method builtins.exec} 1 17.007 17.007 /tmp/perf.py:1() 1 16.996 16.996 /tmp/perf.py:20(main) 36429 0.869 0.000 {method 'join' of 'str' objects} ``` ## snakeviz ```bash $ pip install snakeviz Collecting snakeviz Using cached snakeviz-2.1.0-py2.py3-none-any.whl (282 kB) Collecting tornado>=2.0 Using cached tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl (427 kB) Installing collected packages: tornado, snakeviz Successfully installed snakeviz-2.1.0 tornado-6.1 ``` ## snakeviz ```bash $ snakeviz prof ``` ## snakeviz ![](static/snakeviz-v1.png) ## vprof ``` $ pip install vprof Collecting vprof Using cached vprof-0.38-py3-none-any.whl (319 kB) Collecting psutil>=3 Using cached psutil-5.7.3-cp39-cp39d-linux_x86_64.whl Installing collected packages: psutil, vprof Successfully installed psutil-5.7.3 vprof-0.38 ``` ## vprof ``` $ vprof -c h "perf.py AFPy 0000" ``` ## vprof ![](static/vprof.png) # Le code, v1 ```python [2,5,6] def main(): already_checked = [] while True: c = "".join(choice(ascii_letters) for _ in range(10)) if c in already_checked: continue already_checked.append(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) print("Searching") ``` # Le code, v2 ```python [2,5,6] def main(): already_checked = set() while True: c = "".join(choice(ascii_letters) for _ in range(10)) if c in already_checked: continue already_checked.add(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) print("Searching") ``` ## Les perfs ```bash $ hyperfine 'python perf.py AFPy 00000' ``` - v1 : ∞ - v2 (`set`) : 23 s ± 23 s ::: notes Il existe aussi pyperf: https://github.com/psf/pyperf ## cProfile + pstats ```bash $ python -m cProfile -o prof perf.py AFPy 0000 $ python -m pstats prof ``` ## cProfile + pstats ``` ncalls cumtime percall filename:lineno(function) 12/1 1.156 1.156 {built-in method builtins.exec} 1 1.156 1.156 perf.py:1() 1 1.143 1.143 perf.py:35(main) 34215 0.771 0.000 {method 'join' of 'str' objects} 371647 0.681 0.000 perf.py:39() 337860 0.526 0.000 /python3.9/random.py(choice) 337860 0.283 0.000 /python3.9/random.py(randbelow) 33786 0.134 0.000 built-in method print 372745 0.037 0.000 method 'getrandbits' of Random' 33786 0.037 0.000 method 'hexdigest' of hashlib ``` ## snakeviz ```bash $ snakeviz prof ``` ## snakeviz ![](static/snakeviz-v2.png) ## Le code, v2 ```python [4] def main(): already_checked = set() while True: c = "".join(choice(ascii_letters) for _ in range(10)) if c in already_checked: continue already_checked.add(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) print("Searching") ``` # Le code, v3 ```python [4] def main(): already_checked = set() while True: c = "".join(choices(ascii_letters, k=10)) if c in already_checked: continue already_checked.add(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) print("Searching") ``` ## Les perfs ```bash $ hyperfine 'python perf.py AFPy 00000' ``` - v1 : ∞ - v2 (`set`) : 23 s ± 23 s - v3 (`choices`): 8.591 s ± 6.525 s ## snakeviz ![](static/snakeviz-v3.png) # Le code, v4 ```python [3] def main(): already_checked = set() for c in product(ascii_letters, repeat=10): c = "".join(c) if c in already_checked: continue already_checked.add(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) print("Searching") ``` ## Les perfs ```bash $ hyperfine 'python perf.py AFPy 00000' ``` - v1 : ∞ - v2 (`set`) : 23 s ± 23 s - v3 (`choices`): 8.591 s ± 6.525 s - v4 (`deterministic`) : 3.900 s ± 0.121 s ## snakeviz ![](static/snakeviz-v4.png) # Le code, v5 ```python [12] def main(): already_checked = set() for c in product(ascii_letters, repeat=10): c = "".join(c) if c in already_checked: continue already_checked.add(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) # print("Searching") ``` ## Les perfs ```bash $ hyperfine 'python perf.py AFPy 00000' ``` - v1 : ∞ - v2 (`set`) : 23 s ± 23 s - v3 (`choices`): 8.591 s ± 6.525 s - v4 (`deterministic`) : 3.900 s ± 0.121 s - v5 (`print`) : 3.120 s ± 0.062 s ## Snakeviz ![](static/snakeviz-v5.png) Il reste du `hexdigest`, du `encode`, et du `join`. ## vprof ![](static/vprof2.png) Ligne 26 et 28 !? # Le code, v6 ```python def main(): for c in product(ascii_letters, repeat=10): c = "".join(c) digest = sha512( (c + args.string).encode("UTF-8")).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({c} + {args.string}) = {digest}") sys.exit(0) ``` ## Snakeviz ![](static/snakeviz-v6.png) Il reste du `hexdigest`, du `encode`, et du `join`. # Le code, v7 ```python def main(): string = args.string.encode("UTF-8") pool = ascii_letters.encode("UTF-8") for c in product(pool, repeat=10): digest = sha512(bytes(c) + string).hexdigest() if digest.startswith(args.sha_prefix): print(f"sha512({bytes(c)} + {args.string}) = " f"{digest}") sys.exit(0) ``` ## Les perfs ```bash $ hyperfine 'python perf.py AFPy 00000' ``` - v1 : ∞ - v2 (`set`) : 23 s ± 23 s - v3 (`choices`): 8.591 s ± 6.525 s - v4 (`deterministic`) : 3.900 s ± 0.121 s - v5 (`print`) : 3.120 s ± 0.062 s - v6 (`dead code`): 2.844 s ± 0.059 s - v7 (`bytes`) : 1.837 s ± 0.067 s ## Encore plus d'expériences - pypy: 3.8s - python: 1.8s - cython (hashlib) 1.3s - cython (crypto) 0.8s - c: 0.3s