PDA

Voir la version complète : Optimisation en Python



César Vonc
17/09/2012, 15h24
Bonjour,

Ce sujet a pour but de regrouper les petites astuces permettant d'augmenter la vitesse d'exécution d'un code Python.


Exemple de chronomètre :

import time
import c4d

def chrono():
t = time.time()

for i in xrange(10000000) :
pass

print time.time() - t

if __name__=='__main__':
chrono()
--


Vérifier un booléen :


if a is True :██████████ 0,70 s


if a :
█████ 0,35 s - Gain de 50 %


--


Boucle :


for i in range(10000000) :██████████ 0,84 s


for i in xrange(10000000) :███ 0,24 s - Gain de 71 %

César Vonc
17/09/2012, 15h42
Je découvre des choses intéressantes, si quelqu'un a une explication, elle est la bienvenue :

Créer un vecteur :


v = c4d.Vector(0, 0, 0)██████████ 4,25 s


v = c4d.Vector(0.0, 0.0, 0.0)█████████ 3,86 s - Gain de 10 %


v = c4d.Vector()██████ 2,60 s - Gain de 39 %


Il en va de même pour créer un vecteur quelconque ayant les trois mêmes valeurs, Vector(1.0) sera plus rapide que Vector(1.0, 1.0, 1.0) et Vector(1, 1, 1).


Ma théorie :
Dans le premier cas, j'imagine que Python doit convertir les entiers en flottants, ce qui rend le second cas plus rapide étant donné qu'on entre flottants directement, et le dernier cas n'a probablement pas à vérifier les valeurs entrées, d'où le gain.

xs_yann
18/09/2012, 11h48
Salut,

Super sujet César. :thumbup1:

Voilà un petit bout de code pour comparer le temps d'execution de 2 instructions :


import timeit
import c4d

LOOP_COUNT = 10000000

def compare(stmt1, stmt2):
t1 = timeit.Timer(stmt1, "import c4d").timeit(number=LOOP_COUNT)
t2 = timeit.Timer(stmt2, "import c4d").timeit(number=LOOP_COUNT)
print "Statement 1 : %.3f s" % t1
print "Statement 2 : %.3f s" % t2
faster = 1 if t1 < t2 else 2
if faster == 2:
t1, t2 = t2, t1
diff = t2 - t1
percent = 100.0 - t1 / t2 * 100.0
print "-> Statement %d is %.3f s (%.1f %%) faster" % (faster, diff, percent)


test1 = """
a = 42
if a & 1:
pass
"""

test2 = """
a = 42
if a % 2:
pass
"""

def main():
compare(test1, test2)

Pour le c4d.Vector, je pense que tu as raison, la conversion implicite prend du temps. Cela se vérifie en comparant :

a = 1.0
a += 1.0
plus rapide (~ 10 %) que

a = 1.0
a += 1
Aussi

a = b = c = x
plus rapide (~ 14 %) que

a, b, c = x, y, z
et de ce fait "c4d.Vector(1)" sera quand même plus rapide que "c4d.Vector(1.0, 1.0, 1.0)" mais moins que "c4d.Vector(1.0)"

xs_yann
26/09/2012, 16h06
Attention certains exemples sont destinés à être utilisés uniquement dans le cas d'un problème de rapidité avéré.

Tester la nullité d'un entier :


if a == 0 :██████████ 6,04 s


if not a :█████████ 5,38 s - Gain de 11 %


--


Tester la positivité d'un index inférieur :


if i - 1 > 0:██████████ 0,80 s


if i > 1:████████ 0,60 s - Gain de 25 %


--


Insérer dans un tableau :


tab = []
for i in xrange(5):
tab.append(i) ██████████ 1,44 s


tab = []
append = tab.append
for i in xrange(5):
append(i)█████████ 1,22 s - Gain de 15 %


--


Opérations sur les vecteurs :


v = c4d.Vector(10, 20, 30)
w = c4d.Vector(1, 2, 3)
x = c4d.Vector(v.x * w.x, v.y * w.y, v.z * w.z)
y = c4d.Vector(v.x + w.x, v.y + w.y, v.z + w.z)██████████ 2,75 s


v = c4d.Vector(10, 20, 30)
w = c4d.Vector(1, 2, 3)
x = v ^ w
y = v + w████ 0,99 s - Gain de 64 %



--


Tester si le tableau contient au moins 1 élément :


a = []
if len(a) == 0:██████████ 1,52 s


a = []
if not a:█████ 0,76 s - Gain de 50 %

César Vonc
26/09/2012, 16h37
Excellent, Yann !

Dis donc, je connaissais pas cette formulation :

tab = []
append = tab.append
for i in xrange(5):
append(i) C'est plutôt curieux, append a quel type ?

xs_yann
26/09/2012, 16h42
>>> tab = []
>>> append = tab.append
>>> type(append)
<type 'builtin_function_or_method'>

C'est le même principe que les pointeurs sur fonctions en C++.

Deux liens en anglais avec pleins de conseils :

http://wiki.python.org/moin/PythonSpeed/PerformanceTips
http://stackoverflow.com/questions/7165465/optimizing-python-code

César Vonc
29/09/2012, 17h27
Merci pour les liens.


Division de flottants :


a = 5.0 / 2██████████ 1,00 s


a = 5.0 / 2.0████████ 0,80 s - Gain de 20 %


a = 5.0 * 0.5████ 0,36 s - Gain de 64 %


Préférer la multiplication plutôt que la division tant que possible.
Remplacer 1/3 par 0,333 est préférable si la précision n'est pas de mise.

Note : 5 / 2 renverra un nombre entier (2).

César Vonc
23/01/2014, 13h00
Édit : finalement tout ça était faux, il faut mieux rester avec des vecteurs tout le temps car la conversion de vecteurs en tuples est beaucoup trop longue.