|
|
|
@ -29,10 +29,11 @@ O(cⁿ) Exponentielle
|
|
|
|
|
O(n!) Factorielle
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Il faut les grapher pour s'en rendre compte : cf. include/big.o.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Comparaison asymptotique
|
|
|
|
|
|
|
|
|
|
Exemples.
|
|
|
|
@ -52,7 +53,7 @@ def is_in(a_set: set, a_value):
|
|
|
|
|
return a_value in a_set
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Peu importe la taille de la liste, accéder à un élément prend le même temps.
|
|
|
|
|
|
|
|
|
@ -63,7 +64,7 @@ Attention c'est toujours en base deux.
|
|
|
|
|
|
|
|
|
|
Exemple typique : chercher dans un annuaire.
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Un annuaire deux fois plus gros ne vous demandera pas deux fois plus
|
|
|
|
|
de temps mais peut-être une opération de plus.
|
|
|
|
@ -90,12 +91,12 @@ Typique d'algorithmes de tris.
|
|
|
|
|
|
|
|
|
|
## Les mesures de complexité
|
|
|
|
|
|
|
|
|
|
- De temps (CPU consommé)
|
|
|
|
|
- D'espace (Mémoire consommée)
|
|
|
|
|
- Dans le meilleur des cas
|
|
|
|
|
- Dans le pire des cas
|
|
|
|
|
- Dans le cas moyen
|
|
|
|
|
- Amorti
|
|
|
|
|
- De temps (CPU consommé).
|
|
|
|
|
- D'espace (Mémoire consommée).
|
|
|
|
|
- Dans le meilleur des cas.
|
|
|
|
|
- Dans le pire des cas.
|
|
|
|
|
- Dans le cas moyen.
|
|
|
|
|
- Amorti.
|
|
|
|
|
- ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -119,22 +120,31 @@ Mais retenir par cœur la complexité de quelques structures
|
|
|
|
|
- 1 nanoseconde (1 ns) c'est un milliardième de seconde.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Rappel des unités de temps
|
|
|
|
|
|
|
|
|
|
- milli c'est `10 ** -3`, c'est `0.001`.
|
|
|
|
|
- micro c'est `10 ** -6`, c'est `0.000_001`.
|
|
|
|
|
- nano c'est `10 ** -9`, c'est `0.000_000_001`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Le cas typique
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ python -m timeit -s 'container = list(range(10_000_000))' \
|
|
|
|
|
'10_000_001 in container'
|
|
|
|
|
#!cache -- python -m timeit -s 'container = list(range(10_000_000))' '10_000_001 in container'
|
|
|
|
|
```shell
|
|
|
|
|
$ python -m pyperf timeit \
|
|
|
|
|
> --setup 'container = list(range(10_000_000))' \
|
|
|
|
|
> '10_000_001 in container'
|
|
|
|
|
#!cache -- python -m pyperf timeit --fast -s 'container = list(range(10_000_000))' '10_000_001 in container'
|
|
|
|
|
|
|
|
|
|
$ python -m timeit -s 'container = set(range(10_000_000))' \
|
|
|
|
|
'10_000_001 in container'
|
|
|
|
|
#!cache -- python -m timeit -n 100 -s 'container = set(range(10_000_000))' '10_000_001 in container'
|
|
|
|
|
$ python -m pyperf timeit \
|
|
|
|
|
> --setup 'container = set(range(10_000_000))' \
|
|
|
|
|
> '10_000_001 in container'
|
|
|
|
|
#!cache -- python -m pyperf timeit --fast -s 'container = set(range(10_000_000))' '10_000_001 in container'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Pourquoi une si grande différence !?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
C'est l'heure du live coding !
|
|
|
|
|
|
|
|
|
@ -145,14 +155,14 @@ C'est l'heure du live coding !
|
|
|
|
|
|
|
|
|
|
`time`, un outil POSIX, mais aussi une fonction native de bash :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
```shell
|
|
|
|
|
$ time python -c 'container = set(range(10_000_000))'
|
|
|
|
|
#!cache -- time -p python -c 'container = set(range(10_000_000))' 2>&1
|
|
|
|
|
#!cache -- time -p python -c 'container = set(range(10_000_000))'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Mais `time` ne teste qu'une fois.
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
real 0m0.719s # C'est le temps « sur le mur »
|
|
|
|
|
user 0m0.521s # Temps CPU passé « dans Python »
|
|
|
|
@ -180,7 +190,7 @@ Benchmark 1: python -c pass
|
|
|
|
|
Time (mean ± σ): 19.4 ms ± 0.6 ms
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
N'essayez pas de retenir les chiffres, retenez les faits.
|
|
|
|
|
|
|
|
|
@ -199,7 +209,7 @@ Benchmark 1: /usr/bin/python3.10 -c pass
|
|
|
|
|
Time (mean ± σ): 19.1 ms ± 0.8 ms
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Leur parler de `--enable-optimizations` (PGO).
|
|
|
|
|
|
|
|
|
@ -213,7 +223,7 @@ Timeit c'est dans la stdlib de Python, ça s'utilise en ligne de commande ou dep
|
|
|
|
|
|
|
|
|
|
C'est l'équivalent d'hyperfine mais exécutant du Python plutôt qu'un programme :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
```shell
|
|
|
|
|
$ ~/.local/bin/python3.10 -m pyperf timeit pass
|
|
|
|
|
.....................
|
|
|
|
|
Mean +- std dev: 7.33 ns +- 0.18 ns
|
|
|
|
@ -223,7 +233,7 @@ $ /usr/bin/python3.10 -m pyperf timeit pass
|
|
|
|
|
Mean +- std dev: 6.10 ns +- 0.11 ns
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Avec hyperfine on teste combien de temps ça prend à Python **de
|
|
|
|
|
démarrer** puis d'exécuter `pass`, ici on teste combien de temps ça
|
|
|
|
@ -234,7 +244,7 @@ prend d'exécuter `pass`.
|
|
|
|
|
|
|
|
|
|
time, timeit, hyperfine, pyperf c'est bien pour mesurer, comparer.
|
|
|
|
|
|
|
|
|
|
cProfile nous aider à trouver la fonction coupable dans un script plus gros.
|
|
|
|
|
cProfile nous aider à trouver la fonction coupable.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## cProfile, exemple
|
|
|
|
@ -331,7 +341,7 @@ En cachant `approx_phi` ?
|
|
|
|
|
#!sed -n '10,/return step1/p' include/phi4.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Notez l'astuce pour que le `step2` d'un
|
|
|
|
|
tour soit le `step1` du suivant...
|
|
|
|
@ -346,6 +356,7 @@ $ python -m cProfile --sort cumulative phi4.py 2000
|
|
|
|
|
de `fib` n'est pas chaud, et il peut vite devoir descendre
|
|
|
|
|
profondément en récursion...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## cProfile, exemple
|
|
|
|
|
|
|
|
|
|
Il est temps de sortir une implémentation de `fib` plus robuste, basée
|
|
|
|
@ -363,35 +374,37 @@ $ python -m cProfile --sort cumulative phi5.py 2000
|
|
|
|
|
#!cache -- python -m cProfile --sort cumulative include/phi5.py 2000 | head -n 2 | sed 's/^ *//g;s/seconds/s/g'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
notes:
|
|
|
|
|
|
|
|
|
|
Mieux.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Snakeviz
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
python -m pip install snakeviz
|
|
|
|
|
#!python -m pip install snakeviz >/dev/null 2>&1
|
|
|
|
|
python -m cProfile -o phi5.prof phi5.py 2000
|
|
|
|
|
#!if [ ! -f /tmp/phi5.prof ]; then python -m cProfile -o /tmp/phi5.prof include/phi5.py 2000 >/dev/null 2>&1; fi
|
|
|
|
|
#!if [ ! -f .cache/phi5.prof ]; then python -m cProfile -o .cache/phi5.prof include/phi5.py 2000 >/dev/null 2>&1; fi
|
|
|
|
|
python -m snakeviz phi5.prof
|
|
|
|
|
#!if [ ! -f .cache/phi5-snakeviz.png ]; then python -m snakeviz -s /tmp/phi5.prof & TOKILL=$!; sleep 1; cutycapt --min-width=1024 --delay=500 --url=http://127.0.0.1:8080/snakeviz/%2Ftmp%2Fphi5.prof --out=.cache/phi5-snakeviz.png ; kill $TOKILL; fi
|
|
|
|
|
#!if [ ! -f output/phi5-snakeviz.png ]; then python -m snakeviz -s .cache/phi5.prof & TOKILL=$!; sleep 1; cutycapt --min-width=1024 --delay=500 --url=http://127.0.0.1:8080/snakeviz/%2Ftmp%2Fphi5.prof --out=output/phi5-snakeviz.png ; kill $TOKILL; fi
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Snakeviz
|
|
|
|
|
|
|
|
|
|
![](phi5-snakeviz.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Scalene
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
```shell
|
|
|
|
|
$ python -m pip install scalene
|
|
|
|
|
#!python -m pip install scalene >/dev/null 2>&1
|
|
|
|
|
$ scalene phi5.py 100000
|
|
|
|
|
#!if [ ! -f .cache/phi5.html ]; then scalene include/phi5.py 100000 --html --outfile .cache/phi5.html --cli >&2; fi
|
|
|
|
|
#!if [ ! -f .cache/phi5-scalene.png ]; then cutycapt --min-width=1024 --delay=100 --url=file://$(pwd)/.cache/phi5.html --out=.cache/phi5-scalene.png; fi
|
|
|
|
|
##!if [ ! -f output/phi5.html ]; then scalene include/phi5.py 100000 --html --outfile output/phi5.html --cli >&2; fi
|
|
|
|
|
##!if [ ! -f output/phi5-scalene.png ]; then cutycapt --min-width=1024 --delay=100 --url=file://$(pwd)/output/phi5.html --out=output/phi5-scalene.png; fi
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Scalene
|
|
|
|
|
|
|
|
|
|
![](phi5-scalene.png)
|
|
|
|
@ -401,18 +414,91 @@ $ scalene phi5.py 100000
|
|
|
|
|
|
|
|
|
|
Générateur de prénoms français.
|
|
|
|
|
|
|
|
|
|
::: notes
|
|
|
|
|
|
|
|
|
|
See includes/prenom-*.py
|
|
|
|
|
Notes: voir includes/prenom-*.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## TODO
|
|
|
|
|
- vprof
|
|
|
|
|
- https://pypi.org/project/pyflame/
|
|
|
|
|
- https://pypi.org/project/pp3yflame/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Cython
|
|
|
|
|
|
|
|
|
|
Cython est un dialecte de Python transpilable en C.
|
|
|
|
|
|
|
|
|
|
## Cython démo
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
#!cat include/collatz_length.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Cython démo
|
|
|
|
|
|
|
|
|
|
#!rm -f include/*.so # Ensure we're not hitting the cythonized one here...
|
|
|
|
|
```shell
|
|
|
|
|
$ python -m pyperf timeit \
|
|
|
|
|
> -s 'from collatz_length import collatz_length'
|
|
|
|
|
> 'collatz_length(837799)'
|
|
|
|
|
#!cache -- python -m pyperf timeit --fast --setup 'from include.collatz_length import collatz_length' 'collatz_length(837799)'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ cythonize --inplace collatz_length.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#!cythonize --inplace include/collatz_length.py
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ python -m pyperf timeit \
|
|
|
|
|
> -s 'from collatz_length import collatz_length'
|
|
|
|
|
> 'collatz_length(837799)'
|
|
|
|
|
#!cache -- python -m pyperf timeit --fast -s 'from include.collatz_length import collatz_length' 'collatz_length(837799)' # faster
|
|
|
|
|
#!# Beware, the cythonized use `-s` while the non cythonized uses `--setup` just to have two cache buckets :D
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Cython annotate
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ cython -a collatz_length.py
|
|
|
|
|
#!cython -a include/collatz_length.py
|
|
|
|
|
#!cutycapt --min-width=1024 --delay=500 --url=file://$(pwd)/include/collatz_length.html --out=output/collatz_length.png
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
![](collatz_length.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Cython annotated
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
#!cat include/collatz_length_annotated.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ cythonize --inplace collatz_length_annotated.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#!cythonize --inplace include/collatz_length_annotated.py
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ python -m pyperf timeit \
|
|
|
|
|
> -s 'from collatz_length_annotated import collatz_length'
|
|
|
|
|
> 'collatz_length(837799)'
|
|
|
|
|
#!cache -- python -m pyperf timeit --fast -s 'from include.collatz_length_annotated import collatz_length' 'collatz_length(837799)'
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Cython annotate again
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ cython -a include/collatz_length_annotated.py
|
|
|
|
|
#!cython -a include/collatz_length_annotated.py
|
|
|
|
|
#!cutycapt --min-width=1024 --delay=500 --url=file://$(pwd)/include/collatz_length_annotated.html --out=output/collatz_length_annotated.png
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
![](collatz_length_annotated.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Numba
|
|
|
|
|
|
|
|
|
|
# mypyc
|
|
|
|
|