Heim  >  Artikel  >  Backend-Entwicklung  >  Methoden zur Verbesserung der Python-Leistung

Methoden zur Verbesserung der Python-Leistung

高洛峰
高洛峰Original
2017-02-28 16:46:041156Durchsuche

Einige Lösungen zur Verbesserung der Python-Leistung.

1. Funktionsaufrufoptimierung (Speicherbereich, Zugriff auf Speicher vermeiden)

Der Kernpunkt der Programmoptimierung besteht darin, die Betriebsspanne zu minimieren. einschließlich Code Die Spanne der Ausführungszeit und die Spanne des Speicherplatzes.

1. Für große Datenmengen verwenden Sie 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. Um kleine Datenmengen zu summieren, vermeiden Sie die Verwendung von „sum“. unkompliziert Hohe kumulative Effizienz.

%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
2. Zur Schleifenoptimierung, um Elemente abzurufen (Stack oder Register verwenden, um Speicherzugriff zu vermeiden)


Die Verwendung von Indizes sollte so weit wie möglich vermieden werden.

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

entspricht der direkten Zuweisung eines Werts zu jedem Element.

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

3. Generatoroptimierung (Nachschlagetabelle statt Betrieb)

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

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)
  
4. Optimierung des Leistungsbetriebs (pow( x, y, z))

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)

Fazit: pow(x,y,z) ist besser als x**y%z .

5. Optimierung des Abteilungsbetriebs

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

Fazit: pmod ist besser als // Und % .

6. Zeitkomplexität des Optimierungsalgorithmus
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

Die zeitliche Komplexität des Algorithmus hat den größten Einfluss auf die Ausführungseffizienz des Programms. Sie können wählen geeignete Datenstruktur in Python Um die zeitliche Komplexität zu optimieren, beträgt beispielsweise die zeitliche Komplexität der Suche nach einem bestimmten Element in Liste und Menge O(n) bzw. O(1). Verschiedene Szenarien haben unterschiedliche Optimierungsmethoden. Im Allgemeinen gibt es Ideen wie „Divide and Conquer“, „Branch and Bound“ und „Greedy Dynamic Programming“.

7. Sinnvoller Einsatz von Copy und Deepcopy

Für Objekte mit Datenstrukturen wie dict und list verwendet die direkte Zuweisung Referenzen. In einigen Fällen müssen Sie das gesamte Objekt kopieren. In diesem Fall können Sie copy und deepcopy im Kopierpaket verwenden. Der Unterschied zwischen diesen beiden Funktionen besteht darin, dass deepcopy rekursiv kopiert. Die Effizienz ist unterschiedlich:

Das -n nach timeit gibt die Anzahl der Durchläufe an, und die letzten beiden Zeilen entsprechen der Ausgabe von die beiden timeit, wie folgt gleich. Man erkennt, dass Letzteres um eine Größenordnung langsamer ist.


Ein Beispiel zum Kopieren:

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


Was passiert ist, ist Folgendes: [ []] ist eine Ein-Element-Liste, die eine leere Liste enthält, daher sind alle drei Elemente von [[]] * 3 (zeigen auf) diese leere Liste. Durch Ändern eines Listenelements wird die Liste geändert. Die Modifikationseffizienz ist hoch.

8. Verwenden Sie dict oder set, um Elemente zu finden

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

Python-Wörterbücher und -Sets werden mithilfe von Hash-Tabellen implementiert (ähnlich der C++-Standardbibliothek unordered_map). ) beträgt die zeitliche Komplexität der Suche nach Elementen O(1).



Fazit: set hat den geringsten Speicherbedarf und dict hat die kürzeste Laufzeit.

9. Angemessener Nutzen (Generator) und Ertrag (Speicher sparen)
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

Fazit: Versuchen Sie, Generatoren zum Durchqueren zu verwenden.

Die oben genannten Lösungen zur Verbesserung der Python-Leistung werden in Zukunft weitere hinzufügen. Sie können einen Blick darauf werfen, wenn Sie sie benötigen.

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
Weitere Artikel zu Methoden zur Verbesserung der Python-Leistung finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn