======Python benchmark : accélérer ses calculs======
Tous les résultats sont indiqués en secondes.
La fonction something() consiste généralement au choix d'un nombre flottant aléatoire :
from random import uniform
def something():
return uniform(-100000,100000)
Bien que ne semblant avoir indépendemment que peu d'effets, l'application de l'ensemble de ces astuces dans un programme peut avoir un effet drastique sur son temps d'exécution.
=====Boucles======
====While et for====
Boucle while :
i = 0
while i < x:
something()
i += 1
Boucle for :
for i in range(x):
something()
Boucle for préparée :
rng = range(x)
for i in rng:
something()
===Bench===
Moyenne de 10 essais sur des boucles à 1.000.000 de tours :
- Boucle while : 0.6407999277
- Boucle for : 0.6034000397
- Boucle for préparée : 0,5895000458
0|50x20|100|While:100,For:94,For2:76
Moyenne de 1.000.000 d'essais sur des boucles à 10 tours :
- Boucle while : 0.000006115
- Boucle for : 0.000006264
- Boucle for préparée : 0,000005651
0|50x20|100|While:91,For:100,For2:70
===Conclusion===
La boucle for devant parcourir une liste d'éléments, l'initialisation de ladite liste lui prend du temps, problème que ne rencontre pas la boucle while. La boucle while est donc plus rapide lorsqu'il faut faire beaucoup de fois peu de tours.
Une fois lancée la boucle for se montre par contre bien plus véloce que sa voisine, qui s'encombre à chaque tour de boucle d'une opération sur le compteur. La boucle for est donc plus rapide lorsqu'il faut faire peu de fois beaucoup de tours.
La boule for "préparée", en n'apellant plus la fonction range() à chaque tour, se montre la plus véloce dans toutes les situations.
====Appel de fonction====
Appel dans une boucle :
def doit():
something()
rng = range(x)
for i in rng:
doit()
Boucle dans la fonction :
def doit(x):
rng = range(x)
for i in rng:
something()
doit(x)
===Bench===
1.000.000 de tours :
- Appel dans une boucle : 1.341
- Boucle dans la fonction : 1.164
0|50x20|100|Dans:100,Hors:70
===Conclusion===
L'appel à une fonction prends du temps, il est donc bien plus rapide de mettre la boucle à l'intérieur de la fonction.
=====Dictionnaires, tableaux et listes======
====Initialisation====
Dictionnaire :
for i in range(10000000):
dict = {5:10,1:8,7:2}
Tableau :
for i in range(10000000):
array = [10,8,2]
Liste :
for i in range(10000000):
list = (10,8,2)
- Dictionnaire : 2.356
- Tableau : 2.309
- Liste : 0.639
0|50x20|2.356|dict:2.356,array:2.309,list:0.639
====Récupération d'une valeur====
Dictionnaire :
for i in range(10000000):
dict[1]
Tableau :
for i in range(10000000):
array[1]
Liste :
for i in range(10000000):
list[1]
- Dictionnaire : 0.795
- Tableau : 0.702
- Liste : 0.802
0|50x20|0.802|dict:0.795,array:0.702,list:0.802
=====Imports=====
====From et import====
From :
rng = range(x)
for i in rng:
from math import *
something()
Cette syntaxe (//import *//) n'est correcte qu'au premier niveau du programme. J'ai ici passé outre les alertes afin d'en calculer la vitesse.
From limité :
rng = range(x)
for i in rng:
from math import pi
something()
Import :
rng = range(x)
for i in rng:
import math
something()
===Bench===
1.000.000 de tours :
- From : 12.56
- From limité : 2.99
- Import : 0.99
0|50x20|13|From:12.56,From2:2.99,Import:0.99
===Conclusion===
En cas de besoin ou de volonté d'utiliser la syntaxe //from//, l'import d'éléments précis dans le module s'avère cinq à six fois plus véloce que l'import complet dudit module.
L'utilisation de la syntaxe //import// seule reste quand à elle de 12 à 13 fois plus efficiente.
====Réimporter====
Import multiple :
rng = range(x)
for i in rng:
import math
something()
Import unique :
import math
rng = range(x)
for i in rng:
something()
===Bench===
1.000.000 de tours :
- Import multiple : 9.95
- Import unique : 0.79
0|50x20|10|Multiple:9.95,Unique:0.79
===Conclusion===
Un import peut-être très long (cela dépend de la taille du module), il faut donc toujours éviter d'importer plusieurs fois la même chose, en faisant par exemple :
math = None
rng = range(x)
for i in rng:
if math is None:
import math
something()
=====Opérateurs=====
====Division et multiplication====
Division :
rng = range(x)
for i in rng:
123456.789 / 4
Multiplication :
rng = range(x)
for i in rng:
123456.789 * 0.25
Le résultat de ces calculs est le même.
===Bench===
Calcul effectué 1.000.000 de fois :
- Division : 0.121
- Multiplication : 0.047
0|50x20|12.1|Division:12.1,Multiplication:4.7
===Conclusion===
La multiplication est de deux à quatre fois plus rapide que la division.
====Incrémentation et addition====
Addition :
rng = range(x)
for i in rng:
i = i + 1
Incrémentation :
rng = range(x)
for i in rng:
i += 1
===Bench===
Calcul effectué 1.000.000 de fois :
- Addition : 0.140
- Incrémentation : 0.117
0|50x20|14|Addition:14,Incrémentation:11.7
===Conclusion===
L'incrémentation est légèrement plus rapide que l'addition.
=====Classes====
====Accès aux méthodes====
Point dans la boucle :
arr = []
rng = range(x)
for i in rng:
arr.append(something())
Point hors de la boucle :
arr = []
append = arr.append
rng = range(x)
for i in rng:
append(something())
===Bench===
Calcul effectué 1.000.000 de fois :
- Point dans la boucle : 1.307
- Point hors de la boucle : 1.157
0|50x20|1.3|Dans:1.3,Hors:1.1
===Conclusion===
La référence à une méthode de classe (syntaxe du point), au lieu d'être recalculée à chaque tour de boucle, peut être une fois pour toutes stockée dans une variable, accélérant notablement les calculs.
=====WIP=====
En cours d'écriture.
A venir : benchmark code, parcourt et tri de tableaux/listes et dictionnaires, concaténation et vitesse des opérateurs mathématiques et de comparaison.
~~DISCUSSION~~