L'article que j'ai partagé aujourd'hui ne contient pas beaucoup de texte mais principalement du code. Absolument informatif et simple, il partage principalement 20 conseils pour améliorer les performances de Python et vous apprend à dire adieu au ralentissement de Python. L'auteur original Kaiyuan, un programmeur full-stack, utilise Python, Java, PHP et C.
1. Complexité temporelle de l'algorithme d'optimisation
La complexité temporelle de l'algorithme a le plus grand impact sur l'efficacité d'exécution du programme. choisissez la structure de données appropriée pour optimiser la complexité temporelle. Par exemple, la complexité temporelle de la recherche d'un certain élément dans la liste et l'ensemble est respectivement O(n) et O(1). Différents scénarios ont différentes méthodes d'optimisation. De manière générale, il existe généralement des idées telles que la programmation diviser pour mieux régner, brancher et relier, gourmande et dynamique.
2. Réduisez les données redondantes
Par exemple, utilisez le triangle supérieur ou le triangle inférieur pour enregistrer une grande matrice symétrique. Utilisez une représentation matricielle clairsemée dans les matrices où 0 élément représente la majorité.
3. Utilisation appropriée de la copie et de la copie profonde
Pour les objets avec des structures de données telles que dict et list, l'affectation directe utilise la référence. Dans certains cas, vous devez copier l'intégralité de l'objet. Dans ce cas, vous pouvez utiliser copy et deepcopy dans le package copy. La différence entre ces deux fonctions est que cette dernière copie de manière récursive. L'efficacité est également différente : (Le programme suivant est exécuté en ipython)
import copy a = range(100000) %timeit -n 10 copy.copy(a) # 运行10次 copy.copy(a) %timeit -n 10 copy.deepcopy(a) 10 loops, best of 3: 1.55 ms per loop 10 loops, best of 3: 151 ms per loop
Le -n après timeit indique le nombre d'exécutions, et la dernière deux lignes correspondent à la sortie des deux temps, c'est la même chose ci-dessous. On peut voir que ce dernier est d'un ordre de grandeur plus lent.
4. Utilisez dict ou set pour rechercher des éléments
Python dict et set sont implémentés à l'aide de tables de hachage (similaires à unordered_map dans la bibliothèque standard c 11) pour rechercher des éléments. . La complexité temporelle est O(1).
a = range(1000) s = set(a) d = dict((i,1) for i in a) %timeit -n 10000 100 in d %timeit -n 10000 100 in s 10000 loops, best of 3: 43.5 ns per loop 10000 loops, best of 3: 49.6 ns per loop
dict est légèrement plus efficace (et prend plus de place).
5. Utilisation raisonnable des générateurs et rendement
%timeit -n 100 a = (i for i in range(100000)) %timeit -n 100 b = [i for i in range(100000)] 100 loops, best of 3: 1.54 ms per loop 100 loops, best of 3: 4.56 ms per loop
Ce que vous obtenez en utilisant () est Le L'espace mémoire requis pour un objet générateur n'a rien à voir avec la taille de la liste, l'efficacité sera donc plus élevée. Dans des applications spécifiques, par exemple, set(i for i in range(100000)) sera plus rapide que set([i for i in range(100000)]).
Mais pour les situations où le parcours de boucle est requis :
%timeit -n 10 for x in (i for i in range(100000)): pass %timeit -n 10 for x in [i for i in range(100000)]: pass 10 loops, best of 3: 6.51 ms per loop 10 loops, best of 3: 5.54 ms per loop
Ce dernier est plus efficace, mais s'il y a une rupture dans le loop, Les avantages de l'utilisation de générateurs sont évidents. Yield sert aussi à créer des générateurs :
def yield_func(ls): for i in ls: yield i+1 def not_yield_func(ls): return [i+1 for i in ls] ls = range(1000000) %timeit -n 10 for i in yield_func(ls):pass %timeit -n 10 for i in not_yield_func(ls):pass 10 loops, best of 3: 63.8 ms per loop 10 loops, best of 3: 62.9 ms per loop
Pour les listes qui ne sont pas très volumineuses en mémoire, on peut retourner directement une liste, mais le rendement est meilleur pour plus de lisibilité (préférence personnelle).
Les fonctions de générateur intégrées de Python 2.x incluent la fonction xrange, le package itertools, etc.
6. Optimiser les boucles
Ne mettez pas les choses qui peuvent être faites en dehors de la boucle à l'intérieur de la boucle. Par exemple, l'optimisation suivante peut être deux fois plus rapide :
a = range(10000) size_a = len(a) %timeit -n 1000 for i in a: k = len(a) %timeit -n 1000 for i in a: k = size_a 1000 loops, best of 3: 569 µs per loop 1000 loops, best of 3: 256 µs per loop
7. Optimiser l'ordre des expressions de jugement multiples
Pour et, celle qui est la moins satisfaisante les conditions doivent être placées en premier, pour ou, mettez en premier celle qui remplit le plus de conditions. Par exemple :
a = range(2000) %timeit -n 100 [i for i in a if 10 < i < 20 or 1000 < i < 2000] %timeit -n 100 [i for i in a if 1000 < i < 2000 or 100 < i < 20] %timeit -n 100 [i for i in a if i % 2 == 0 and i > 1900] %timeit -n 100 [i for i in a if i > 1900 and i % 2 == 0] 100 loops, best of 3: 287 µs per loop 100 loops, best of 3: 214 µs per loop 100 loops, best of 3: 128 µs per loop 100 loops, best of 3: 56.1 µs per loop
8. Utilisez join pour fusionner les chaînes dans l'itérateur
.In [1]: %%timeit ...: s = '' ...: for i in a: ...: s += i ...: 10000 loops, best of 3: 59.8 µs per loop In [2]: %%timeit s = ''.join(a) ...: 100000 loops, best of 3: 11.8 µs per loop
join a une amélioration d'environ 5 fois dans la méthode d'accumulation.
9. Choisissez la méthode de formatage des caractères appropriée
s1, s2 = 'ax', 'bx' %timeit -n 100000 'abc%s%s' % (s1, s2) %timeit -n 100000 'abc{0}{1}'.format(s1, s2) %timeit -n 100000 'abc' + s1 + s2 100000 loops, best of 3: 183 ns per loop 100000 loops, best of 3: 169 ns per loop 100000 loops, best of 3: 103 ns per loop
Dans les trois cas, la méthode % It est le plus lent, mais la différence entre les trois n'est pas grande (tous sont très rapides). (Personnellement, je pense que % est le plus lisible)
10. Échangez les valeurs de deux variables sans utiliser de variables intermédiaires
In [3]: %%timeit -n 10000 a,b=1,2 ....: c=a;a=b;b=c; ....: 10000 loops, best of 3: 172 ns per loop In [4]: %%timeit -n 10000 a,b=1,2a,b=b,a ....: 10000 loops, best of 3: 86 ns per loop
Utilisez a,b=b,a au lieu de c=a;a=b;b=c; pour échanger les valeurs de a,b, ce qui peut être plus de 1 fois plus rapide.
11. Utiliser if is
a = range(10000) %timeit -n 100 [i for i in a if i == True] %timeit -n 100 [i for i in a if i is True] 100 loops, best of 3: 531 µs per loop 100 loops, best of 3: 362 µs per loop
utiliser if is True est presque un plus rapide que if == Des temps vrais.
12. Utiliser la comparaison en cascade x
13. while 1 est plus rapide que while True
x, y, z = 1,2,3 %timeit -n 1000000 if x < y < z:pass %timeit -n 1000000 if x < y and y < z:pass 1000000 loops, best of 3: 101 ns per loop 1000000 loops, best of 3: 121 ns per loop
tandis que 1 est beaucoup plus rapide que while true , La raison est que dans python2.x, True est une variable globale, pas un mot-clé.
14. Utilisez ** au lieu de pow
def while_1(): n = 100000 while 1: n -= 1 if n <= 0: break def while_true(): n = 100000 while True: n -= 1 if n <= 0: break m, n = 1000000, 1000000 %timeit -n 100 while_1() %timeit -n 100 while_true() 100 loops, best of 3: 3.69 ms per loop 100 loops, best of 3: 5.61 ms per loop
** qui est plus de 10 fois plus rapide !
15. Utilisez cProfile, cStringIO et cPickle pour implémenter la même fonction
(correspondant respectivement aux packages profile, StringIO, pickle)%timeit -n 10000 c = pow(2,20) %timeit -n 10000 c = 2**20 10000 loops, best of 3: 284 ns per loop 10000 loops, best of 3: 16.9 ns per loop
Package implémenté en c, plus de 10 fois plus rapide !
16. Utilisez la meilleure méthode de désérialisation
import cPickle import pickle a = range(10000) %timeit -n 100 x = cPickle.dumps(a) %timeit -n 100 x = pickle.dumps(a) 100 loops, best of 3: 1.58 ms per loop 100 loops, best of 3: 17 ms per loop
Ce qui suit compare l'efficacité des méthodes eval, cPickle et json pour désérialiser les chaînes correspondantes :
On peut voir que json est près de 3 fois plus rapide que cPickle et plus de 20 fois plus rapide que eval.
17. Utilisation des extensions C (Extension)
import json import cPickle a = range(10000) s1 = str(a) s2 = cPickle.dumps(a) s3 = json.dumps(a) %timeit -n 100 x = eval(s1) %timeit -n 100 x = cPickle.loads(s2) %timeit -n 100 x = json.loads(s3) 100 loops, best of 3: 16.8 ms per loop 100 loops, best of 3: 2.02 ms per loop 100 loops, best of 3: 798 µs per loop
Il existe actuellement trois méthodes principales : CPython (la méthode d'implémentation la plus courante de python) API native, ctypes, Cython, et cffi , leur fonction est de permettre aux programmes Python d'appeler des bibliothèques de liens dynamiques compilées à partir de C. Leurs caractéristiques sont :
API native CPython : En introduisant le fichier d'en-tête Python.h, les structures de données Python peuvent être utilisées directement dans le programme C correspondant. Le processus de mise en œuvre est relativement lourd, mais son champ d’application est relativement large.
ctypes : est généralement utilisé pour envelopper (envelopper) des programmes C, permettant aux programmes Python purs d'appeler des fonctions dans des bibliothèques de liens dynamiques (dll sous Windows ou alors fichiers sous Unix). Si vous souhaitez utiliser une bibliothèque C existante en python, l'utilisation de ctypes est un bon choix. Selon certains tests de référence, les ctypes python2 sont la méthode la plus performante.
Cython : Cython est un sur-ensemble de CPython qui simplifie le processus d'écriture d'extensions C. L'avantage de Cython est que sa syntaxe est concise et qu'elle est bien compatible avec les bibliothèques comme numpy qui contiennent un grand nombre d'extensions C. Les scénarios habilitants de Cython visent généralement l'optimisation d'un certain algorithme ou processus du projet. Dans certains tests, les améliorations des performances peuvent être des centaines de fois supérieures.
cffi : cffi est l'implémentation de ctypes dans pypy (voir ci-dessous pour plus de détails), et est également compatible avec CPython. cffi fournit un moyen d'utiliser les bibliothèques de classes C en python. Vous pouvez écrire du code C directement dans le code python et prendre en charge les liens vers les bibliothèques de classes C existantes.
L'utilisation de ces méthodes d'optimisation vise généralement à optimiser les modules de goulot d'étranglement des performances des projets existants, ce qui peut améliorer considérablement l'efficacité opérationnelle de l'ensemble du programme avec une petite quantité de modifications par rapport au projet d'origine.
18. Programmation parallèle
En raison de l'existence de GIL, il est difficile pour Python de tirer pleinement parti des processeurs multicœurs. Cependant, les modes parallèles suivants peuvent être obtenus grâce au module multitraitement intégré :
Processus multiples : Pour les programmes gourmands en CPU, vous pouvez utiliser les modes Process et Multiprocessing Pool Attendez que les classes encapsulées implémentent le calcul parallèle via plusieurs processus. Cependant, étant donné que le coût de communication dans le processus est relativement élevé, l'efficacité des programmes qui nécessitent une grande quantité d'interactions de données entre les processus peut ne pas être grandement améliorée.
Multi-threading : Pour les programmes gourmands en E/S, le module multiprocessing.dummy utilise l'interface multiprocessing pour encapsuler le threading, ce qui rend la programmation multithread très simple (par exemple, vous peut utiliser l'interface de la carte Pool, simple et efficace).
Distribué : La classe Managers en multitraitement fournit un moyen de partager des données entre différents processus, et des programmes distribués peuvent être développés sur cette base.
Dans différents scénarios commerciaux, vous pouvez en choisir un ou une combinaison de plusieurs pour optimiser les performances du programme.
19. Le tueur ultime : PyPy
PyPy est Python implémenté à l'aide de RPython (un sous-ensemble de CPython). Selon les données de test de référence sur le site officiel, il). est meilleur que l'implémentation de Python par CPython est plus de 6 fois plus rapide. La raison pour laquelle il est rapide est qu'il utilise un compilateur Just-in-Time (JIT), c'est-à-dire un compilateur dynamique. Différent des compilateurs statiques (tels que gcc, javac, etc.), il utilise les données du processus en cours d'exécution. du programme d'optimisation. Pour des raisons historiques, le GIL est toujours conservé dans pypy, mais le projet STM en cours tente de transformer PyPy en Python sans le GIL.
Si le programme python contient des extensions C (non-cffi), l'effet d'optimisation de JIT sera considérablement réduit, encore plus lent que CPython (que Numpy). Donc, dans PyPy, il est préférable d'utiliser du Python pur ou d'utiliser l'extension cffi.
Avec l'amélioration de STM, Numpy et d'autres projets, je pense que PyPy remplacera CPython.
20. Utiliser des outils d'analyse des performances
En plus du module timeit utilisé dans ipython ci-dessus, il existe également cProfile. L'utilisation de cProfile est également très simple : python -m cProfile filename.py est le nom de fichier du programme à exécuter. Vous pouvez voir le nombre de fois que chaque fonction est appelée et la durée d'exécution dans la sortie standard. pour trouver les goulots d'étranglement des performances du programme peuvent ensuite être optimisés de manière ciblée.
Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'apprentissage de chacun. J'espère également que tout le monde soutiendra le site Web PHP chinois.
Pour plus de 20 conseils pour faire voler votre Python et des articles connexes, veuillez faire attention au site Web PHP chinois !