Heim >Backend-Entwicklung >Python-Tutorial >Mehrere Methoden zur Verbesserung der Python-Leistung

Mehrere Methoden zur Verbesserung der Python-Leistung

WBOY
WBOYOriginal
2016-08-04 08:55:451597Durchsuche

Einige Lösungen zur Verbesserung der Python-Leistung.

1. Funktionsaufrufoptimierung (Speicherplatzspanne, Speicherzugriff vermeiden)

Der Kernpunkt der Programmoptimierung besteht darin, die Operationsspanne zu minimieren, einschließlich der Codeausführungszeit und der Speicherkapazität.

1. Big-Data-Summierung, Summe verwenden

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 Daten zu summieren, vermeiden Sie die Verwendung von 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

Fazit: Die Summe der großen Datenmengen ist hocheffizient, und die Summe der kleinen Datenmengen ist bei der direkten Akkumulation hocheffizient.

2. Zur Schleifenoptimierung, um Elemente abzurufen (Stack oder Register verwenden, um Speicherzugriff zu vermeiden)

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

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

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

Es entspricht der direkten Zuweisung eines Werts zu jedem Element.

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. Generatoroptimierung (Nachschlagetabelle statt Betrieb)

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. Optimierung des Leistungsbetriebs (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

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

5. Optimierung des Abteilungsbetriebs

In [1]: from random import getrandbits
 
In [2]: x = getrandbits(4096)
 
In [3]: y = getrandbits(2048)
 
In [4]: %timeit -n 10000 q, r = divmod(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

Fazit: divmod ist besser als // und %.

6. Zeitliche Komplexität des Optimierungsalgorithmus

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

7. Angemessener Einsatz von Copy und Deepcopy

Für Objekte von Datenstrukturen wie Diktat und Liste verwendet die direkte Zuweisung eine Referenz. 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. Effizienz ist anders:

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

Das -n nach timeit gibt die Anzahl der Durchläufe an. Die letzten beiden Zeilen entsprechen der Ausgabe der beiden timeit, das gleiche unten. Man erkennt, dass Letzteres um eine Größenordnung langsamer ist.

Ein Beispiel zum Thema Kopieren:

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

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

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

Python-Wörterbücher und -Sets werden mithilfe von Hash-Tabellen implementiert (ähnlich der C-Standardbibliothek unordered_map), und die Zeitkomplexität zum Suchen von Elementen beträgt 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

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

9. Angemessener Nutzen (Generator) und Ertrag (Speichereinsparung)

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

Fazit: Versuchen Sie, Generatoren zum Durchqueren zu verwenden.

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

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