Heim > Artikel > Backend-Entwicklung > Effizienter Python-Code
Meiner Meinung nach ist die Python-Community in drei Schulen unterteilt, nämlich die Python 2.x-Organisation, die 3.x-Organisation und die PyPy-Organisation. Diese Klassifizierung läuft im Wesentlichen auf die Kompatibilität und Geschwindigkeit der Bibliothek hinaus. Dieser Artikel konzentriert sich auf einige gängige Techniken zur Codeoptimierung und die erhebliche Leistungsverbesserung nach dem Kompilieren in C. Natürlich werde ich auch die Laufzeit der drei wichtigsten Python-Genres angeben. Mein Ziel ist es nicht, zu beweisen, dass eines besser ist als das andere, sondern Ihnen nur eine Vorstellung davon zu geben, wie Sie diese spezifischen Beispiele in verschiedenen Kontexten vergleichen können.
Verwendung des Generators
Eine häufig übersehene Speicheroptimierung ist die Verwendung von Generatoren. Mit Generatoren können wir eine Funktion erstellen, die jeweils nur einen Datensatz zurückgibt, anstatt alle Datensätze auf einmal. Wenn Sie python2.x verwenden, verwenden Sie deshalb xrange anstelle von range oder ifilter anstelle von filter. Ein gutes Beispiel ist das Erstellen einer großen Liste und das Zusammenfügen dieser Listen.
import timeit import random def generate(num): while num: yield random.randrange(10) num -= 1 def create_list(num): numbers = [] while num: numbers.append(random.randrange(10)) num -= 1 return numbers print(timeit.timeit("sum(generate(999))", setup="from __main__ import generate", number=1000)) >>> 0.88098192215 #Python 2.7 >>> 1.416813850402832 #Python 3.2 print(timeit.timeit("sum(create_list(999))", setup="from __main__ import create_list", number=1000)) >>> 0.924163103104 #Python 2.7 >>> 1.5026731491088867 #Python 3.2
Das geht nicht nur etwas schneller, es verhindert auch, dass Sie die gesamte Liste im Speicher speichern müssen
Einführung in Ctypes
Für wichtige Leistungscodes stellt uns Python selbst auch eine API zum Aufrufen von C-Methoden zur Verfügung, hauptsächlich über ctypes. Sie können ctypes verwenden, ohne C-Code zu schreiben. Standardmäßig stellt Python eine vorkompilierte Standard-C-Bibliothek bereit. Kehren wir zum Generatorbeispiel zurück und sehen, wie viel Zeit es dauert, es mithilfe von Ctypes zu implementieren.
import timeit from ctypes import cdll def generate_c(num): #Load standard C library libc = cdll.LoadLibrary("libc.so.6") #Linux #libc = cdll.msvcrt #Windows while num: yield libc.rand() % 10 num -= 1 print(timeit.timeit("sum(generate_c(999))", setup="from __main__ import generate_c", number=1000)) >>> 0.434374809265 #Python 2.7 >>> 0.7084300518035889 #Python 3.2
Ersetzte es einfach durch eine Zufallsfunktion von c und die Laufzeit wurde um mehr als die Hälfte reduziert! Wenn ich Ihnen jetzt sage, dass wir es besser machen können, würden Sie es glauben?
Einführung in Cython
Cython ist eine Obermenge von Python, die es uns ermöglicht, C-Funktionen aufzurufen und Variablen zu deklarieren, um die Leistung zu verbessern. Bevor wir versuchen, es zu verwenden, müssen wir Cython installieren.
sudo pip install cython
Cython ist im Wesentlichen ein Zweig einer anderen Pyrex-ähnlichen Bibliothek, die sich nicht mehr in der Entwicklung befindet. Es kompiliert unseren Python-ähnlichen Code in eine C-Bibliothek, die wir in einer Python-Datei aufrufen können. Verwenden Sie das Suffix .pyx anstelle des Suffixes .py für Ihre Python-Dateien. Sehen wir uns an, wie Sie unseren Generatorcode mit Cython ausführen.
#cython_generator.pyx import random def generate(num): while num: yield random.randrange(10) num -= 1
Wir müssen eine setup.py erstellen, damit wir Cython dazu bringen können, unsere Funktion zu kompilieren.
from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( cmdclass = {'build_ext': build_ext}, ext_modules = [Extension("generator", ["cython_generator.pyx"])] )
Kompilieren mit:
python setup.py build_ext --inplace
Sie sollten zwei Dateien sehen können: die Datei cython_generator.c und die Datei generator.so. Wir verwenden die folgende Methode, um unser Programm zu testen:
import timeit print(timeit.timeit("sum(generator.generate(999))", setup="import generator", number=1000)) >>> 0.835658073425
Nicht schlecht, mal sehen, ob es etwas gibt, das wir verbessern können. Wir können „num“ zunächst als Ganzzahl deklarieren und dann die Standard-C-Bibliothek importieren, die für unsere Zufallsfunktion verantwortlich ist.
#cython_generator.pyx cdef extern from "stdlib.h": int c_libc_rand "rand"() def generate(int num): while num: yield c_libc_rand() % 10 num -= 1
Wenn wir kompilieren und erneut ausführen, werden wir diese erstaunliche Zahl sehen.
>>> 0.033586025238
Nur ein paar Änderungen brachten ordentliche Ergebnisse. Manchmal ist diese Änderung jedoch mühsam. Sehen wir uns also an, wie man sie mit normalem Python durchführt.
Einführung in PyPy PyPy ist ein Just-in-Time-Compiler für Python 2.7.3. Laienhaft ausgedrückt bedeutet dies, dass Ihr Code schneller ausgeführt wird. Quora verwendet PyPy in der Produktion. Auf der Download-Seite von PyPy gibt es einige Installationsanweisungen. Wenn Sie jedoch Ubuntu verwenden, können Sie es über apt-get installieren. Die Funktionsweise ist sofort einsatzbereit, also kein verrücktes Bashen oder Ausführen von Skripten, einfach herunterladen und ausführen. Sehen wir uns an, wie sich unser ursprünglicher Generatorcode unter PyPy verhält.
import timeit import random def generate(num): while num: yield random.randrange(10) num -= 1 def create_list(num): numbers = [] while num: numbers.append(random.randrange(10)) num -= 1 return numbers print(timeit.timeit("sum(generate(999))", setup="from __main__ import generate", number=1000)) >>> 0.115154981613 #PyPy 1.9 >>> 0.118431091309 #PyPy 2.0b1 print(timeit.timeit("sum(create_list(999))", setup="from __main__ import create_list", number=1000)) >>> 0.140175104141 #PyPy 1.9 >>> 0.140514850616 #PyPy 2.0b1
Wow! Ohne Änderung einer einzigen Codezeile ist die Laufgeschwindigkeit achtmal schneller als bei der reinen Python-Implementierung.
Weitere Tests Warum weitere Forschung? PyPy ist der Champion! Nicht ganz wahr. Obwohl die meisten Programme auf PyPy laufen können, werden einige Bibliotheken nicht vollständig unterstützt. Darüber hinaus ist es einfacher, C-Erweiterungen für Ihr Projekt zu schreiben, als den Compiler zu ändern. Lassen Sie uns etwas tiefer gehen und sehen, wie ctypes es uns ermöglicht, Bibliotheken in C zu schreiben. Testen wir die Geschwindigkeit der Zusammenführungssortierung und der Berechnung der Fibonacci-Folge. Das Folgende ist der C-Code (functions.c), den wir verwenden werden:
/* functions.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* http://rosettacode.org/wiki/Sorting_algorithms/Merge_sort#C */ inline void merge (int *left, int l_len, int *right, int r_len, int *out) { int i, j, k; for (i = j = k = 0; i < l_len && j < r_len;) out[k++] = left[i] < right[j] ? left[i++] : right[j++]; while (i < l_len) out[k++] = left[i++]; while (j < r_len) out[k++] = right[j++]; } /* inner recursion of merge sort */ void recur (int *buf, int *tmp, int len) { int l = len / 2; if (len <= 1) return; /* note that buf and tmp are swapped */ recur (tmp, buf, l); recur (tmp + l, buf + l, len - l); merge (tmp, l, tmp + l, len - l, buf); } /* preparation work before recursion */ void merge_sort (int *buf, int len) { /* call alloc, copy and free only once */ int *tmp = malloc (sizeof (int) * len); memcpy (tmp, buf, sizeof (int) * len); recur (buf, tmp, len); free (tmp); } int fibRec (int n) { if (n < 2) return n; else return fibRec (n - 1) + fibRec (n - 2); }
Auf der Linux-Plattform können wir es mit der folgenden Methode in eine gemeinsam genutzte Bibliothek kompilieren:
gcc -Wall -fPIC -c functions.c gcc -shared -o libfunctions.so functions.o
Mit ctypes können Sie diese Bibliothek verwenden, indem Sie die gemeinsam genutzte Bibliothek „libfunctions.so“ laden, genau wie wir es zuvor mit der Standard-C-Bibliothek getan haben. Hier vergleichen wir die Python-Implementierung und die C-Implementierung. Jetzt beginnen wir mit der Berechnung der Fibonacci-Folge:
# functions.py from ctypes import * import time libfunctions = cdll.LoadLibrary("./libfunctions.so") def fibRec(n): if n < 2: return n else: return fibRec(n-1) + fibRec(n-2) start = time.time() fibRec(32) finish = time.time() print("Python: " + str(finish - start)) # C Fibonacci start = time.time() x = libfunctions.fibRec(32) finish = time.time() print("C: " + str(finish - start))
Wie erwartet ist C schneller als Python und PyPy. Auf die gleiche Weise können wir auch Zusammenführungssortierungen vergleichen.
Wir haben uns noch nicht mit der Cypes-Bibliothek befasst, daher spiegeln diese Beispiele nicht die leistungsstarke Seite von Python wider. Die Cypes-Bibliothek weist nur wenige Standardtypbeschränkungen auf, wie z. B. int, char array, float, bytes usw. Standardmäßig gibt es kein ganzzahliges Array. Durch Multiplikation mit c_int (ctype ist der Typ int) können wir jedoch indirekt ein solches Array erhalten. Dies wird auch in Zeile 7 des Codes angezeigt. Wir haben ein c_int-Array erstellt, ein Array unserer Zahlen, und sie in ein c_int-Array
gepackt主要的是c语言不能这样做,而且你也不想。我们用指针来修改函数体。为了通过我们的c_numbers的数列,我们必须通过引用传递merge_sort功能。运行merge_sort后,我们利用c_numbers数组进行排序,我已经把下面的代码加到我的functions.py文件中了。
#Python Merge Sort from random import shuffle, sample #Generate 9999 random numbers between 0 and 100000 numbers = sample(range(100000), 9999) shuffle(numbers) c_numbers = (c_int * len(numbers))(*numbers) from heapq import merge def merge_sort(m): if len(m) <= 1: return m middle = len(m) // 2 left = m[:middle] right = m[middle:] left = merge_sort(left) right = merge_sort(right) return list(merge(left, right)) start = time.time() numbers = merge_sort(numbers) finish = time.time() print("Python: " + str(finish - start)) #C Merge Sort start = time.time() libfunctions.merge_sort(byref(c_numbers), len(numbers)) finish = time.time() print("C: " + str(finish - start))
Python: 0.190635919571 #Python 2.7 Python: 0.11785483360290527 #Python 3.2 Python: 0.266992092133 #PyPy 1.9 Python: 0.265724897385 #PyPy 2.0b1 C: 0.00201296806335 #Python 2.7 + ctypes C: 0.0019741058349609375 #Python 3.2 + ctypes C: 0.0029308795929 #PyPy 1.9 + ctypes C: 0.00287103652954 #PyPy 2.0b1 + ctypes
这儿通过表格和图标来比较不同的结果。
Merge Sort | Fibonacci | |
---|---|---|
Python 2.7 | 0.191 | 1.187 |
Python 2.7 + ctypes | 0.002 | 0.044 |
Python 3.2 | 0.118 | 1.272 |
Python 3.2 + ctypes | 0.002 | 0.046 |
PyPy 1.9 | 0.267 | 0.564 |
PyPy 1.9 + ctypes | 0.003 | 0.048 |
PyPy 2.0b1 | 0.266 | 0.567 |
PyPy 2.0b1 + ctypes | 0.003 | 0.046 |
Das obige ist der detaillierte Inhalt vonEffizienter Python-Code. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!