Bei der Implementierung in Python ist die Verarbeitungszeit möglicherweise zu lang, um die Anforderungen zu erfüllen. In diesem Fall werden in diesem Artikel vier Arten von Gegenmaßnahmen gezeigt.
Wenn Sie etwas tun, um die Dinge zu beschleunigen, verlieren Sie etwas anderes. Typische Beispiele für Kompromisse sind Meinungsfreiheit, Lesbarkeit, Abhängigkeit, Speichernutzung und CPU-Auslastung. Bitte folgen Sie der Nutzung und Kapazität und verwenden Sie diese korrekt.
Vor dem Beschleunigen muss außerdem mithilfe eines Profiling-Tools oder dergleichen überprüft werden, was im Skript langsam ist. Selbst wenn Sie Ihr Bestes tun, um die nicht langsame Verarbeitung zu beschleunigen, hat dies nur geringe Auswirkungen auf die Gesamtausführungszeit. Dieser Artikel erklärt die Vorgehensweise nicht.
In diesem Artikel werden die folgenden vier Arten von Beschleunigungsmethoden vorgestellt.
Ursprünglich entsprechen andere Maßnahmen wie "Verwenden einer Bibliothek" und "Erstellen einer Bibliothek" auch dem "Umschreiben eines Skripts". Hier Pythons Sprachspezifikation und Standard Library Die folgenden Methoden werden in der Kategorie /index.html) vorgestellt. Es fügt der Abhängigkeit nicht viel hinzu (obwohl es von der Python-Version abhängen kann), da dies in der Standardbibliothekskategorie möglich ist.
Die Optimierung des Python-Interpreters macht nicht viel. Es gibt viele Möglichkeiten, dasselbe zu tun, sodass Sie einen schnelleren Weg wählen können, um es zu beschleunigen.
In einer tatsächlichen Anwendung macht der hier angegebene Unterschied im Schreibstil jedoch keinen großen Unterschied. Wenn Sie nicht an eine Konvertierung denken können oder die langsame Verarbeitung wahrscheinlich woanders stattfindet.
Wenn Sie Staub aufhäufen, können Sie das natürlich tun. Wenn Sie beispielsweise versuchen, eine Datenbank nur mit Python zu implementieren, ist die hier gezeigte Langsamkeit der Verarbeitung effektiv und insgesamt unbrauchbar.
Das Hinzufügen von Ganzzahlen von 1 zu n ist beispielsweise "(1) Hinzufügen mit" "(2) Integrierte Summe Wenn Sie es auf drei Arten implementieren, "use /functions.html#sum)" und "(3) benutze die Summenformel" und die Zeit messen, wird es wie folgt sein.
In [1]: %%timeit
...: sum = 0
...: for n in range(1000000):
...: sum += n
87.3 ms ± 492 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [2]: %%timeit
...: sum(range(1000000))
33.3 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [3]: %%timeit
...: ((1000000-1)*1000000)//2
13 ns ± 0.538 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
In diesem Beispiel ist die Summenformel schnell, aber Sie haben nicht immer eine solche Konvertierung.
Beim Generieren der Liste "(1) [Notation der Listeneinbeziehung](https://docs.python.org/ja/3/reference/expressions.html?highlight=%E5%86%85%E5%8C%" Verwenden Sie 85 # Displays-for-Lists-Sets-and-Dictionaries) "" (2) anhängen in einer for-Schleife " "(3) Cache anhängen" "(4) Verwenden Sie die integrierte Funktion Liste" Wenn Sie es mit implementieren und die Zeit messen, sieht es wie folgt aus.
In [1]: %%timeit
...: l = [n for n in range(1000000)]
81.7 ms ± 1.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [2]: %%timeit
...: l = []
...: for n in range(1000000):
...: l.append(n)
130 ms ± 2.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [3]: %%timeit
...: l = []
...: l_append = l.append
...: for n in range(1000000):
...: l_append(n)
97.9 ms ± 2.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [4]: %%timeit
...: l = list(range(1000000))
58.1 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In diesem Beispiel wird nur eine Liste erstellt. Im Allgemeinen sollten Sie jedoch vor dem Anhängen einige "Aufgaben" ausführen. Da diese "verschiedene Verarbeitung" oft langsamer ist, ist es effektiver, die "verschiedene Verarbeitung" zu beschleunigen.
Algorithmen und Datenstrukturen sind Bereiche, die seit der Antike untersucht wurden. Wenn der Berechnungsbetrag in der Auftragsnotation $ O (n) $ und $ O (n ^ 2) $ beträgt, ist die Ausführungszeit völlig anders. Sie können den Algorithmus und die Datenstruktur selbst implementieren oder die in der Standardbibliothek enthaltenen Convenience-Klassen verwenden.
Hier einige Beispiele für Algorithmen und Datenstrukturen in der Standardbibliothek.
Wenn Sie mehrmals feststellen möchten, ob eine Liste ein bestimmtes Element enthält, legen Sie die Liste [set] fest (https://docs.python.org/ja/3/library/stdtypes.html#set- Es ist schneller, in types-set-frozenset zu konvertieren und dann mit in einzuchecken. Wenn Sie nur einmal urteilen, ist die Verarbeitungszeit für die Konvertierung in einen Satz wirksam.
In [1]: %%timeit
...: l = list(range(10000))
...: for n in range(10000):
...: n in l
1.04 s ± 19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [2]: %%timeit
...: l = list(range(10000))
...: s = set(l)
...: for n in range(10000):
...: n in s
1.45 ms ± 14.5 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Dieses Mal habe ich nur 10.000 Mal nach einer Liste mit 10.000 Elementen gesucht und dieses Ergebnis erhalten. Ich denke, dass diese Menge an Erkundungen in tatsächlichen Anwendungen häufig vorkommt.
Sie können collection.deque verwenden, wenn Sie einen Wert mehrmals am Anfang der Liste hinzufügen möchten.
In [1]: from collections import deque
In [2]: %%timeit
...: l = []
...: for n in range(100000):
...: l.insert(0, n)
6.29 s ± 28.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [3]: %%timeit
...: dq = deque()
...: for n in range(100000):
...: dq.appendleft(n)
11.7 ms ± 93.1 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Eine Liste in Python ähnelt einem Array mit variabler Länge. Der Inhalt des Arrays ist sehr vielseitig, da keine Annahmen wie Typ oder Reihenfolge vorliegen, aber Raum für Beschleunigung und Effizienz bei der spezifischen Verarbeitung besteht. Zusätzlich zu collection.deque heapq und bisect Funktionen zum Behandeln von Sammlungen (Datensammlungen) wie library / bisect.html) sind in der Standardbibliothek enthalten.
Das einmal durchgeführte Ergebnis der Berechnung wird im Speicher gespeichert und wiederverwendet. Die aus der Datenbank abgerufenen Daten liegen im Format pickle vor. Sie können es durch [Dumping] beschleunigen (https://docs.python.org/ja/3/library/pickle.html#pickle.dump). Alles, was immer das gleiche Ergebnis zurückgibt, wenn die Argumente zusammen sind, wie z. B. eine mathematische Funktion, kann zwischengespeichert werden. In einigen Fällen ist es schneller, eine lokale Datei zu lesen, als mit der Datenbank zu kommunizieren.
Python verfügt über eine Standardbibliothek, die mit einem Dekorator namens functools.lru_cache definiert wurde, um die Funktion zu notieren. .. Wenn Sie dies verwenden, wird das Ergebnis der Berechnung einmal zwischengespeichert, und wenn die Argumente identisch sind, wird es wiederverwendet.
In [1]: from functools import lru_cache
In [2]: def fib(n):
...: return n if n <= 1 else fib(n-2) + fib(n-1)
In [3]: %%timeit
...: fib(30)
448 ms ± 4.22 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [4]: @lru_cache()
...: def fib(n):
...: return n if n <= 1 else fib(n-2) + fib(n-1)
In [5]: %%timeit -n1 -r1
...: fib(30)
24.2 μs ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
@lru_cache Es ist einfach, weil Sie nur einen Dekorateur hinzufügen müssen. Sie möchten die Fibonacci-Sequenz nicht wirklich berechnen, und wenn Sie möchten, haben Sie möglicherweise eine andere Implementierung. Ist es ein typisches Anwendungsbeispiel, wenn Sie das Integral durch maschinelles Lernen berechnen möchten?
Standardbibliothek Threading und Multiprocessing. Beschleunigen Sie die Verwendung von HTML) oder die Parallelisierung mit gleichzeitig, das in Python 3.2 hinzugefügt wurde. Verwenden Sie Threading, wenn die CPU nicht der Engpass ist, und andernfalls Multiprocessing. Threading ist einfacher, Daten gemeinsam zu nutzen, aber GIL (Global Interpreter Lock) kann die 100% ige CPU-Barriere nicht überwinden. Wenn Sie also die Multi-Core-CPU verbrauchen möchten, sollten Sie Multiprocessing verwenden.
Es ist einfach, concurrent.future zu verwenden. Mit ProcessPoolExecutor können Sie in mehreren Prozessen parallel ausführen. Es gibt auch einen Multithread-ThreadPoolExecutor (https://docs.python.org/ja/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor).
from time import sleep
from random import uniform
def f(x):
sleep(uniform(0, 1) * 10)
return x
from concurrent.futures import ProcessPoolExecutor, Future, wait
with ProcessPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(f, x) for x in range(10)]
done, not_done = wait(futures)
results = [future.result() for future in futures]
Machen Sie einfach den Prozess, den Sie parallelisieren möchten, zu einer Funktion, geben Sie die Anzahl der Worker an und rufen Sie ihn auf. Erinnern wir uns daran als festen Satz.
Wenn Sie es multiprozessiv gestalten möchten, können Sie Ihr Bestes mit Shell anstelle von Python geben. Multi-Prozess kann realisiert werden, indem die obige Funktion f über die Befehlszeile ausführbar gemacht und parallel über die Shell aufgerufen wird.
Angenommen, die Eingabedatei lautet wie folgt (Eingabe).
input
0
1
2
3
4
5
6
7
8
9
Verwenden Sie den Befehl split, um entsprechend zu teilen.
$ split -l 1 input
Führen Sie das Python-Skript mit der geteilten Datei als Eingabe aus.
$ for file in `ls x*`; do python f.py ${file}&; done
Kombinieren Sie die geteilten Ausführungsergebnisse.
$ cat x*.result > result
Sie können alles in Python erledigen, aber je nach Situation können Sie es einfach zu mehreren Prozessen machen, indem Sie einfach & in die Shell einfügen.
Python hat viele Standardbibliotheken und es gibt viele weitere Bibliotheken von Drittanbietern. Bibliotheken von Drittanbietern werden auf PyPI veröffentlicht und können einfach mit dem Befehl pip installiert werden.
Numerische Berechnungen können mit hoher Geschwindigkeit durchgeführt werden, indem eine Bibliothek verwendet wird, die auf numerische Berechnungen wie NumPy spezialisiert ist. Es gibt Bibliotheken, die für bestimmte Berechnungen optimiert sind, nicht nur für numerische Berechnungen. CuPy erleichtert die Durchführung von GPU-Berechnungen aus Python mit CUDA.
NumPy kann die Berechnung von Matrizen und Vektoren beschleunigen. Es kann nicht nur schneller sein, sondern der Code ist auch einfacher und verständlicher. Hier wird jedes Element der Liste, das aus 100.000 Elementen besteht, in das Bild einer 100 × 1000-Matrix eingefügt.
In [1]: import numpy as np
In [2]: %%timeit
...: X = range(100000)
...: Y = range(100000)
...: Z = [x + y for x, y in zip(X, Y)]
13.7 ms ± 140 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [3]: %%timeit
...: X = np.arange(100000)
...: Y = np.arange(100000)
...: Z = X + Y
416 μs ± 2.33 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Sie können sehen, dass NumPy überwältigend schneller ist. Listen berücksichtigen Elementtypen und Listenmethoden, die sich ändern, aber NumPy hat feste Typen und BLAS usw. Sie können beschleunigen, weil Sie die Bibliothek von verwenden können.
Je nachdem, in was die Bibliothek implementiert ist, kann es zu Geschwindigkeitsunterschieden kommen. In der Python 2-Reihe, die in Kürze eingestellt wird, pickle und [cPickle](https: //) Es gab zwei Bibliotheken (docs.python.org/ja/2.7/library/pickle.html#module-cPickle). Es gibt einen beträchtlichen Unterschied in der Ausführungszeit, je nachdem, welche verwendet wird. In der Python 3-Serie wird cPickle jetzt intern aufgerufen, wenn beim Importieren von pickle anstelle des Namens _pickle library cPickle verfügbar ist.
In [1]: import pickle
In [2]: import cPickle
In [3]: l = range(1000000)
In [4]: %%timeit
...: with open("pickle.pkl", "wb") as f:
...: pickle.dump(l, f)
1 loops, best of 3: 3.75 s per loop
In [5]: %%timeit
...: with open("cPickle.pkl", "wb") as f:
...: cPickle.dump(l, f)
1 loops, best of 3: 376 ms per loop
Da es sich um IPython vom Typ Python 2 handelt, unterscheidet sich die Anzeige von den anderen, beträgt jedoch etwa das Zehnfache des Ausführungszeitverhältnisses.
Moderne CPUs verfügen über verschiedene Beschleunigungsfunktionen. Es gibt einen Unterschied in der Ausführungszeit, abhängig davon, ob die Beschleunigungsfunktion selbst aktiviert ist oder die Bibliothek, die die Beschleunigungsfunktion gut verwenden kann, aufgerufen wird. Auf der folgenden Seite finden Sie eine Erklärung.
Wenn Sie bereits eine Python-Bibliothek haben, können Sie diese einfach installieren und verwenden, aber Sie haben nicht immer eine Python-Bibliothek für das, was Sie tun möchten. Darüber hinaus gibt es einige nutzlose Prozesse, die nicht durch Schreiben in Python gelöst werden können. Wenn Sie dennoch beschleunigen möchten, können Sie eine der folgenden Methoden verwenden.
Python hat die Möglichkeit, eine Bibliothek aus einer Windows-DLL-Datei oder einer Linux-Datei zu laden und die Funktionen in der Bibliothek (ctypes auszuführen. html)) hat eine Standardbibliothek. Es kann verwendet werden, wenn es bereits eine Implementierung gibt, die beim Schreiben in Python eine langsame Verarbeitung mit hoher Geschwindigkeit ausführt, es jedoch nicht möglich ist, sie von Python aus aufzurufen.
Die Bibliothek math von Python verfügt nicht über eine cbrt-Funktion für kubische Wurzeln, sondern über [libm](http: //www.c). -tipsref.com/reference/math.html). Gehen Sie wie folgt vor, um es aufzurufen:
In [1]: from ctypes import cdll, c_double
In [2]: libm = cdll.LoadLibrary("libm.so.6")
In [3]: libm.cbrt.restype = c_double
In [4]: libm.cbrt.argtypes = (c_double,)
In [5]: libm.cbrt(8.0)
Out[5]: 2.0
Ich bin mit diesem Beispiel nicht zufrieden, aber ich hoffe, Sie verstehen, dass es einfach ist, es anzurufen. Das Drehen auf der Python-Seite ist langsam, sodass Sie es beschleunigen können, indem Sie eine Funktion aufrufen, die auch den Vorgang des Drehens nach ausführt.
Mit Pythons C-Erweiterungen können Sie Bibliotheken erstellen, die mit C- oder C ++ - Sprachen aus Python aufgerufen werden können. Python ist wie ein Wrapper für eine Bibliothek, die in erster Linie in C / C ++ erstellt wurde. Erstellen wir also eine Seite, die vom Wrapper aufgerufen wird.
Erstellen wir eine Additionsfunktion mit der Erweiterung C. Schreiben Sie zunächst den folgenden Quellcode in C-Sprache.
samplemodule.c
#include <Python.h>
static PyObject * sample_add(PyObject *self, PyObject *args) {
int x, y, z;
if (!PyArg_ParseTuple(args, "ii", &x, &y)) {
return NULL;
}
z = x + y;
return PyLong_FromLong(z);
}
static PyObject *SampleError;
static PyMethodDef SampleMethods[] = {
{"add", sample_add, METH_VARARGS, "Sum x and y then return the sum."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef samplemodule = {
PyModuleDef_HEAD_INIT, "sample", NULL, -1, SampleMethods
};
PyMODINIT_FUNC PyInit_sample(void) {
PyObject *m;
m = PyModule_Create(&samplemodule);
if (m == NULL) {
return NULL;
}
SampleError = PyErr_NewException("sample.error", NULL, NULL);
Py_XINCREF(SampleError);
if (PyModule_AddObject(m, "error", SampleError) < 0) {
Py_XDECREF(SampleError);
Py_CLEAR(SampleError);
Py_DECREF(m);
return NULL;
}
return m;
}
Geben Sie die Python-Headerdatei an und kompilieren Sie mit gcc.
$ gcc -I`python -c 'from distutils.sysconfig import *; print(get_python_inc())'` -DPIC -shared -fPIC -o sample.so samplemodule.c
Nachdem sample.so abgeschlossen ist, starten Sie den Python-Interpreter (IPython) und laden Sie ihn.
In [1]: import sample
In [2]: sample.add(1, 2)
Out[2]: 3
Mit Boost.Python können Sie eine Python-Bibliothek mit weniger Code implementieren. Sie müssen sich nicht zu viele Gedanken über Unterschiede in der Python-Version machen. Stattdessen hängt es von der Boost-Bibliothek ab, sodass die Python-Bibliothek in einer Umgebung ohne die Boost-Bibliothek nicht geladen wird. Eine mit Boost.Python erstellte Bibliothek ist je nach Implementierung genauso schnell wie in C implementiert.
Erstellen wir eine Additionsfunktion mit Boost.Python. Schreiben Sie zunächst den folgenden Quellcode in C ++. Das ist es.
#include <boost/python.hpp>
int add(int x, int y) {
return x + y;
}
BOOST_PYTHON_MODULE(sample) {
using namespace boost::python;
def("add", &add);
}
Geben Sie die Python-Headerdatei und Boost.Python an und kompilieren Sie mit g ++.
$ g++ -I`python -c 'from distutils.sysconfig import *; print(get_python_inc())'` -lboost_python -DPIC -shared -fPIC -o sample.so sample.cpp
Nachdem sample.so abgeschlossen ist, starten Sie den Python-Interpreter (IPython) und laden Sie ihn.
In [1]: import sample
In [2]: sample.add(1, 2)
Out[2]: 3
Die Menge an Code ist viel kürzer als das Schreiben von Grund auf in C-Sprache. Es ist schön.
Mit Cython können Sie Quellcode in einer leicht modifizierten Python-Sprache schreiben und Bibliotheken so schnell generieren wie die in C. Das Verfahren besteht darin, eine Pyx-Datei in einer Python-ähnlichen Sprache zu erstellen, den Quellcode der C-Sprache zu generieren und diese zu kompilieren.
Erstellen wir eine Funktion in Cython, um die Fibonacci-Sequenz zu berechnen. Erstellen Sie zunächst eine Pyx-Datei. Es hat fast die gleiche Grammatik wie Python, mit ein paar Typinformationen.
fib.pyx
def fib(int n):
return n if n <= 1 else fib(n-2) + fib(n-1)
Konvertieren Sie die Pyx-Datei mit dem Befehl cython in den Quellcode der Sprache C. Dann kompiliere mit gcc.
$ cython fib.pyx
$ gcc -I`python -c 'from distutils.sysconfig import *; print(get_python_inc())'` -DPIC -shared -fPIC -o fib.so fib.c
Nachdem fib.so erstellt wurde, starten Sie den Python-Interpreter (IPython) und laden Sie ihn.
In [1]: from fib import fib
In [2]: %%timeit
...: fib(30)
304 ns ± 2.53 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Als ich alles in Python schrieb, war es "448 ms ± 4,22 ms", was eine erhebliche Verbesserung war.
Bisher haben wir eine Bibliothek in C-Sprache erstellt, um dies zu beschleunigen. Das Erstellen einer Bibliothek erforderte Kenntnisse der Erweiterungen von C und Cython für Python sowie das Erlernen anderer Sprachen als Python. Das Erlernen einer neuen Sprache kann jedoch eine entmutigende Aufgabe sein. Hier werden die folgenden Techniken vorgestellt, mit denen vorhandene Python-Programme so erweitert werden können, wie sie sind.
Numba verwendet LLVM, um Bytecode aus LLRM IR für die Geschwindigkeit zu generieren. Wie Sie in Was Sie tun können, bevor ein Python-Skript ausgeführt wird - Qiita sehen können, ist Python eine Skriptsprache vom Typ VM mit Zwischenanweisungen (Bytecode). Der Prozess wird durch Ausführen realisiert.
Laut dem Guide auf der ursprünglichen Site wird nicht nur der Bytecode, sondern auch der Computer optimiert. Es scheint eine Anweisung zu generieren und einen Funktionsaufruf zu ersetzen. Es ist wunderbar ...
In [1]: @jit
...: def fib(n):
...: return n if n <= 1 else fib(n-2) + fib(n-1)
In [9]: %%timeit
...: fib(30)
12.6 ms ± 187 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
PyPy ist ein in Python geschriebenes Python-Verarbeitungssystem. ~~ RPython, das etwas eingeschränktere Funktionen als Python hat und leicht zu optimieren ist, funktioniert. Es ist in ~~ RPython implementiert und mit Python 2.7 und Python 3.6 kompatibel. Laut Python-Kompatibilität - PyPy, Django und Flask Es wurde bestätigt, dass bekannte Bibliotheken wie /) funktionieren, und laut PyPy Speed scheint die Ausführungsgeschwindigkeit recht gut zu sein.
In diesem Artikel habe ich die folgenden Beschleunigungsmethoden vorgestellt.
Welche Methode für den Umgang mit einem Prozess geeignet ist, der Ihrer Meinung nach langsam ist, hängt von der Zeit und dem Fall ab. Es wäre schön, die optimale Methode anwenden zu können, nachdem verschiedene Optionen in Betracht gezogen wurden.
Ich habe nur Beispiele für Fibonacci-Sequenzen angegeben, aber das Finden von Fibonatch-Sequenzen ist keine häufige Aufgabe für Anwendungen. Cython ist möglicherweise schneller als Numba, abhängig von der Verarbeitung, die Sie beschleunigen möchten. Dieses Mal verwende ich die Wiederholung, um die Fibonacci-Sequenz zu finden, aber wenn Sie die Wiederholung zuerst entfernen, ist sie schneller ...
Möge Ihre Qualität des Python-Lebens wunderbar sein.
Beim Schreiben dieses Artikels habe ich auf den folgenden Artikel verwiesen.
Außerdem habe ich mich beim Erlernen von Python anderen wunderbaren Artikeln und Büchern als den oben genannten verschrieben. Es tut mir sehr leid, dass ich es hier nicht vorstellen kann. Vielen Dank für die Veröffentlichung und Veröffentlichung großartiger Informationen. Ich glaube, dass großartige Informationen für diejenigen nützlich sein werden, die Python lernen.
Recommended Posts