Maison >développement back-end >Tutoriel Python >Méthodes d'amélioration des performances Python

Méthodes d'amélioration des performances Python

高洛峰
高洛峰original
2017-02-28 16:46:041173parcourir

Quelques solutions pour améliorer les performances de Python.

1. Optimisation des appels de fonction (étendue de l'espace, éviter d'accéder à la mémoire)

Le point central de l'optimisation du programme est de minimiser la durée des opérations, y compris le code L'étendue du temps d'exécution et l'étendue de l'espace mémoire.

1. Pour la somme du Big Data, utilisez sum

a = range(100000)
%timeit -n 10 sum(a)
10 loops, best of 3: 3.15 ms per loop
%%timeit
  ...: s = 0
  ...: for i in a:
  ...:  s += i
  ...:
100 loops, best of 3: 6.93 ms per loop

2. Pour additionner de petites données, évitez d'utiliser sum

%timeit -n 1000 s = a + b + c + d + e + f + g + h + i + j + k # 数据量较小时直接累加更快
1000 loops, best of 3: 571 ns per loop
%timeit -n 1000 s = sum([a,b,c,d,e,f,g,h,i,j,k]) # 小数据量调用 sum 函数,空间效率降低
1000 loops, best of 3: 669 ns per loop

Conclusion : la sommation de données volumineuses est très efficace et la sommation de petites données est simple Efficacité cumulée élevée.

2. Optimisation de la boucle For pour obtenir des éléments (utilisez la pile ou le registre pour éviter l'accès à la mémoire)

for lst in [(1, 2, 3), (4, 5, 6)]: # lst 索引需要额外开销
  pass

L'utilisation d'index doit être évitée autant que possible.

for a, b, c in [(1, 2, 3), (4, 5, 6)]: # better
  pass

équivaut à attribuer directement une valeur à chaque élément.

def force():
 lst = range(4)
 for a1 in [1, 2]:
   for a2 in lst:
     for a3 in lst:
       for b1 in lst:
         for b2 in lst:
           for b3 in lst:
             for c1 in lst:
               for c2 in lst:
                 for c3 in lst:
                   for d1 in lst:
                     yield (a1, a2, a3, b1, b2, b3, c1, c2, c3, d1)
                      
%%timeit -n 10
for t in force():
  sum([t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9]])
10 loops, best of 3: 465 ms per loop
%%timeit -n 10
for a1, a2, a3, b1, b2, b3, c1, c2, c3, d1 in force():
  sum([a1, a2, a3, b1, b2, b3, c1, c2, c3, d1])
10 loops, best of 3: 360 ms per loop

3. Optimisation du générateur (table de recherche au lieu d'opération)

def force(start, end): # 用于密码暴力破解程序
  for i in range(start, end):
    now = i
    sublst = []
    for j in range(10):
      sublst.append(i % 10) # 除法运算开销较大,比乘法大
      i //= 10
    sublst.reverse()
    yield(tuple(sublst), now)

def force(): # better
 lst = range(5)
 for a1 in [1]:
   for a2 in lst:
     for a3 in lst:
       for b1 in lst:
         for b2 in lst:
           for b3 in lst:
             for c1 in lst:
               for c2 in lst:
                 for c3 in lst:
                   for d1 in lst:
                     yield (a1, a2, a3, b1, b2, b3, c1, c2, c3, d1)
  

r0 = [1, 2] # 可读性与灵活性
r1 = range(10)
r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = r1
force = ((a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
      for a0 in r0 for a1 in r1 for a2 in r2 for a3 in r3 for a4 in r4
      for a5 in r5 for a6 in r6 for a7 in r7 for a8 in r8 for a9 in r9)

4. Optimisation du fonctionnement électrique (pow( x, y, z))

def isprime(n):
  if n & 1 == 0:
    return False
  k, q = find_kq(n)
  a = randint(1, n - 1)
  if pow(a, q, n) == 1: # 比使用 a ** q % n 运算优化数倍
    return True
  for j in range(k):
    if pow(a, pow(2, j) * q, n) == n - 1: # a **((2 ** j) * q) % n
      return True
  return False

Conclusion : pow(x,y,z) est meilleur que x**y%z .

5. Optimisation du fonctionnement de la division

In [1]: from random import getrandbits
 
In [2]: x = getrandbits(4096)
 
In [3]: y = getrandbits(2048)
 
In [4]: %timeit -n 10000 q, r = pmod(x, y)
10000 loops, best of 3: 10.7 us per loop
 
In [5]: %timeit -n 10000 q, r = x//y, x % y
10000 loops, best of 3: 21.2 us per loop

Conclusion : pmod est meilleur que //. et % .

6. 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. Vous pouvez choisir le. structure de données appropriée en python 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 diviser pour régner, brancher et lier et programmation dynamique gourmande.

7. Utilisation raisonnable 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 la copie profonde est récursive. L'efficacité est différente :

In [23]: import copy
In [24]: %timeit -n 10 copy.copy(a)
10 loops, best of 3: 606 ns per loop
In [25]: %timeit -n 10 copy.deepcopy(a)
10 loops, best of 3: 1.17 us per loop

Le -n après timeit indique le nombre d'exécutions, et les deux dernières lignes correspondent à la sortie de les deux fois, comme suit pareil. On peut voir que ce dernier est d'un ordre de grandeur plus lent.

Un exemple de copie :

>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

Ce qui s'est passé est ceci, [ []] est une liste à un élément contenant une liste vide, donc les trois éléments de [[]] * 3 sont (pointent vers) cette liste vide. La modification de n'importe quel élément des listes modifie la liste. L'efficacité des modifications est élevée.

8. Utilisez dict ou set pour rechercher des éléments

Les dictionnaires et ensembles Python sont implémentés à l'aide de tables de hachage (similaires à la bibliothèque standard C unordered_map ), la complexité temporelle de la recherche des éléments est O(1).

In [1]: r = range(10**7)
In [2]: s = set(r) # 占用 588MB 内存
In [3]: d = dict((i, 1) for i in r) # 占用 716MB 内存
In [4]: %timeit -n 10000 (10**7) - 1 in r
10000 loops, best of 3: 291 ns per loop
In [5]: %timeit -n 10000 (10**7) - 1 in s
10000 loops, best of 3: 121 ns per loop
In [6]: %timeit -n 10000 (10**7) - 1 in d
10000 loops, best of 3: 111 ns per loop

Conclusion : set a la plus petite empreinte mémoire et dict a la durée d'exécution la plus courte.

9. Utilisation raisonnable (générateur) et rendement (économie de mémoire)

In [1]: %timeit -n 10 a = (i for i in range(10**7)) # 生成器通常遍历更高效
10 loops, best of 3: 933 ns per loop
In [2]: %timeit -n 10 a = [i for i in range(10**7)]
10 loops, best of 3: 916 ms per loop
In [1]: %timeit -n 10 for x in (i for i in range(10**7)): pass
10 loops, best of 3: 749 ms per loop
In [2]: %timeit -n 10 for x in [i for i in range(10**7)]: pass
10 loops, best of 3: 1.05 s per loop

Conclusion : essayez d'utiliser des générateurs pour parcourir.

Voici quelques solutions pour améliorer les performances de Python. Nous continuerons à en ajouter d'autres à l'avenir. Vous pouvez y jeter un œil si vous en avez besoin.

Pour plus d'articles liés aux méthodes d'amélioration des performances de Python, veuillez faire attention au site Web PHP chinois !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn