[PYTHON] Unterschied in der Ausführungsgeschwindigkeit abhängig vom Schreiben der Cython-Funktion

Zweck

Ich habe untersucht, wie sich die Ausführungsgeschwindigkeit ändert, je nachdem, wie Cython-Funktionen geschrieben werden.

Geschichte

Hintergrund

Methode

# Faktor Entscheidungen
1 Zu verwendende Syntax ① Python für Anweisung ② Cython für Anweisung
2 Argumenttyp (1) Beliebig iterierbar (2) Numpy Array (3) Typisierte Speicheransicht
3 Spezifikation des Argumentelementtyps ① Keine ② Ja
4 Elementzugriffsmethode ① Nicht-Index-Spezifikation (v in vs) ② Index-Spezifikation (vs)[i])
5 Vorhandensein oder Nichtvorhandensein einer Prüffunktion ① Keine ② Ja

Ergebnis

Einzelheiten

1. Vorbereitung

Nehmen Sie die Summe der Quadrate der nächsten Anzahl von Spalten. Bereiten Sie zwei Versionen der Python-Liste und des Numpy-Arrays vor.

python


import numpy as np
vs_list = [1.0,]*10**6
vs_ndarray = np.ones((10**6,), dtype=np.double)

2. Python für Anweisung

Die Leistung der ursprünglichen Python for-Schleife war wie folgt [^ 2]. Es dauert 300-400 ms, um die Summe der Quadrate von $ 10 ^ 6 $ reellen Zahlen zu erhalten [^ 3]. ** In Python scheint es schneller zu sein, den Iterator gehorsam ohne Indexzugriff zu verwenden **.

# Syntax Argumenttyp Elementtypspezifikation Elementzugriff Funktion prüfen Ausführungszeit[ms]
1 Python für Anweisung Beliebig iterierbar Keiner v in vs Ja 302
2 Python für Anweisung Beliebig iterierbar Keiner vs[i] Ja 381

1


def sum_py(vs):
    s = 0
    for v in vs:
        s += v
    return s

python


%timeit square_sum_py(vs_list)

2


def square_sum_py_range(vs):
    s = 0
    for i in range(len(vs)):
        s += vs[i]**2
    return s

python


%timeit square_sum_py_range(vs_list)

3. Numpy Sendung

Es überrascht nicht, dass Numpys Sendung die Dinge dramatisch beschleunigen kann. Cython wird jedoch tatsächlich verwendet, wenn die Broadcast-Funktion nicht verwendet werden kann. Daher müssen hier andere Methoden in Betracht gezogen werden. Sie können sehen, dass die Verarbeitung eines Numpy-Arrays mit einer Python for-Anweisung es verlangsamt.

# Syntax Argumenttyp Elementtypspezifikation Elementzugriff Funktion prüfen Ausführungszeit[ms]
3 Numpy Sendung Numpy Array Ja Keine (unnötig) Ja 17
4 Python für Anweisung Numpy Array Ja v in vs Ja 1640
5 Python für Anweisung Numpy Array Ja vs[i] Ja 1950

3


def square_sum_np(vs):
    return np.sum(vs**2)

python


%timeit square_sum_np(vs_ndarray)

4


#Funktion oben definiert
%timeit square_sum_py(vs_ndarray)

5


#Funktion oben definiert
%timeit square_sum_py_range(vs_ndarray)

4. Angabe des Argumenttyps

Lassen Sie uns als nächstes über die Verwendung von Cython nachdenken, um Pythons for-Anweisung zu beschleunigen. Die Möglichkeit, ein auf Speicher zugängliches Array auf niedriger Ebene an eine Funktion in Cython zu übergeben, besteht darin, das Numpy-Array direkt als Argument anzugeben (http://docs.cython.org/en/latest/src/tutorial/numpy). Es gibt .html) und So geben Sie eine typisierte Speicheransicht als Argument an. Hier beginnen wir mit der intuitiveren früheren Methode. Wenn man sich die fragmentarischen Informationen im Netz ansieht, scheint es, dass nur die Typspezifikation für die Beschleunigung mit Cython effektiv ist. Wie Sie in der folgenden Tabelle sehen können, ist die Angabe der Funktionsargumente in einem Numpy-Array jedoch nicht viel schneller als der ursprüngliche Python-Code (1) (6). Zu diesem Zeitpunkt ist die Angabe der Elementtypen des Numpy-Arrays wenig sinnvoll (7). Natürlich ist es mit Numpy-Arrays schneller als Python-Code (4), aber das ist alles, es gibt keinen Grund, Cython zu verwenden.

# Syntax Argumenttyp Elementtypspezifikation Elementzugriff Funktion prüfen Ausführungszeit[ms]
6 Cython zur Aussage Numpy Array Keiner v in vs Ja 378
7 Cython zur Aussage Numpy Array Ja v in vs Ja 362

6


%%cython
cimport numpy as np
def square_sum_cy_np(np.ndarray vs):
    cdef double v, s = 0.0
    for v in vs:
        s += v**2
    return s

python


%timeit square_sum_cy_np(vs_ndarray)

7


%%cython
cimport numpy as np
def square_sum_cy_np_typed(np.ndarray[np.double_t, ndim=1] vs):
    cdef double v, s = 0.0
    for v in vs:
        s += v**2
    return s

python


%timeit square_sum_cy_np_typed(vs_ndarray)

5. Indexzugriff

Anschließend müssen Sie die Zugriffsmethode (wie die for-Anweisung geschrieben wird) in das Array-Element ändern. Der Indexzugriff ohne Elementtypisierung ist langsamer, der Indexzugriff mit Elementtypisierung jedoch erheblich schneller.

# Syntax Argumenttyp Elementtypspezifikation Elementzugriff Funktion prüfen Ausführungszeit[ms]
8 Cython zur Aussage Numpy Array Keiner vs[i] Ja 1610
9 Cython zur Aussage Numpy Array Ja vs[i] Ja 28

8


%%cython
cimport numpy as np
def square_sum_cy_np_range(np.ndarray vs):
    cdef double s = 0.0
    for i in range(vs.shape[0]):
        s += vs[i]**2
    return s

python


%timeit square_sum_cy_np_range(vs_ndarray)

9


%%cython
cimport numpy as np
def square_sum_cy_np_typed_range(np.ndarray[np.double_t, ndim=1] vs):
    cdef double s = 0.0
    for i in range(vs.shape[0]):
        s += vs[i]**2
    return s

python


%timeit square_sum_cy_np_typed_range(vs_ndarray)

6. Wegfall der Prüffunktion

In der offiziellen Dokumentation wird auch beschrieben, wie die Überprüfungsfunktion für den Array-Zugriff (10-13) weggelassen wird. Der Unterschied zum Nichtauslassen der Prüffunktion (6-9) war jedoch gering. Es ist wahr, dass 10 bis 20% schneller sind, aber es ist nur so effektiv wie der letzte Stoß.

# Syntax Argumenttyp Elementtypspezifikation Elementzugriff Funktion prüfen Ausführungszeit[ms]
10 Cython zur Aussage Numpy Array Keiner v in vs Keiner 315
11 Cython zur Aussage Numpy Array Ja v in vs Keiner 313
12 Cython zur Aussage Numpy Array Keiner vs[i] Keiner 1610
13 Cython zur Aussage Numpy Array Ja vs[i] Keiner 25

10


%%cython
cimport numpy as np
from cython import boundscheck, wraparound
def square_sum_cy_np_nocheck(np.ndarray vs):
    cdef double v, s = 0.0
    with boundscheck(False), wraparound(False):
        for v in vs:
            s += v**2
        return s

python


%timeit square_sum_cy_np_nocheck(vs_ndarray)

11


%%cython
cimport numpy as np
from cython import boundscheck, wraparound
def square_sum_cy_np_typed_nocheck(np.ndarray[np.double_t, ndim=1] a):
    cdef double d, s = 0.0
    with boundscheck(False), wraparound(False):
        for d in a:
            s += d**2
    return s

python


%timeit square_sum_cy_np_typed_nocheck(vs_ndarray)

12


%%cython
cimport numpy as np
from cython import boundscheck, wraparound
def square_sum_cy_np_range_nocheck(np.ndarray a):
    cdef double s = 0.0
    with boundscheck(False), wraparound(False):
        for i in range(a.shape[0]):
            s += a[i]**2
    return s

python


%timeit square_sum_cy_np_range_nocheck(vs_ndarray)

13


%%cython
cimport numpy as np
from cython import boundscheck, wraparound
def square_sum_cy_np_typed_range_nocheck(np.ndarray[np.double_t, ndim=1] a):
    cdef double s = 0.0
    with boundscheck(False), wraparound(False):
        for i in range(a.shape[0]):
            s += a[i]**2
    return s

python


%timeit square_sum_cy_np_typed_range_nocheck(vs_ndarray)

Typisierte Speicheransicht

Eine andere Möglichkeit, ein auf Speicher zugängliches Array auf niedriger Ebene an die Cython-Funktion zu übergeben, besteht darin, eine typisierte Speicheransicht als Argument anzugeben. Diese Methode wird in den offiziellen Dokumenten empfohlen. Wie Sie den folgenden Ergebnissen entnehmen können, funktioniert in diesem Fall auch die Methode für den Zugriff auf die Array-Elemente.

# Syntax Argumenttyp Elementtypspezifikation Elementzugriff Funktion prüfen Ausführungszeit[ms]
14 Cython zur Aussage Typisierte Speicheransicht Ja (notwendig) v in vs Ja 519
15 Cython zur Aussage Typisierte Speicheransicht Ja (notwendig) vs[i] Ja 26
16 Cython zur Aussage Typisierte Speicheransicht Ja (notwendig) v in vs Keiner 516
17 Cython zur Aussage Typisierte Speicheransicht Ja (notwendig) vs[i] Keiner 24

14


%%cython
def square_sum_cy_mv(double[:] vs):
    cdef double v, s = 0.0
    for v in vs:
        s += v**2
    return s

python


%timeit square_sum_cy_np_typed_range_nocheck(vs_ndarray)

15


%%cython
def square_sum_cy_mv_range(double[:] vs):
    cdef double s = 0.0
    for i in range(vs.shape[0]):
        s += vs[i]**2
    return s

python


%timeit square_sum_cy_mv_range(vs_ndarray)

16


%%cython
from cython import boundscheck, wraparound
def square_sum_cy_mv_nocheck(double[:] vs):
    cdef double v, s = 0.0
    with boundscheck(False), wraparound(False):
        for v in vs:
            s += v**2
    return s

python


%timeit square_sum_cy_mv_nocheck(vs_ndarray)

17


%%cython
from cython import boundscheck, wraparound
def square_sum_cy_mv_range_nocheck(double[:] vs):
    cdef double s = 0.0
    with boundscheck(False), wraparound(False):
        for i in range(vs.shape[0]):
            s += vs[i]**2
    return s

python


%timeit square_sum_cy_mv_range_nocheck(vs_ndarray)

Erwägung

Bemerkungen

python


import numpy as np
%load_ext Cython

python


vs = np.ones((10**3,10**3), dtype=np.double)

python


%%cython
from cython import boundscheck, wraparound
cdef double square_sum(double[:, :] vs):
# def square_sum(double[:, :] vs):Auch wenn es in diesem Fall fast gleich ist
    cdef:
        double s = 0.0
        Py_ssize_t nx = vs.shape[0]
        Py_ssize_t ny = vs.shape[1]
        Py_ssize_t i, j
    with boundscheck(False), wraparound(False):
        for i in range(nx):
            for j in range(ny):
                s += vs[i, j]**2
    return s

python


%timeit square_sum(vs)

[^ 1]: Die Anzahl der Codes beträgt nicht $ 2 \ times3 \ times2 \ times2 \ times2 = 24 $, da (1) eine Korrelation zwischen den Auswahlmöglichkeiten besteht und (2) der Referenzcode hinzugefügt wird. Es ist die Ursache. [^ 2]: Es gab eine maximale Abweichung von ± 20% der gemessenen Werte um% timeit, aber hier sind wir besorgt über den Unterschied von mehreren bis mehreren zehn Mal, also bin ich besorgt über den Unterschied von mehreren Prozent. Ich werde nicht. [^ 3]: Die CPU verwendet Intel Celeron.

Recommended Posts

Unterschied in der Ausführungsgeschwindigkeit abhängig vom Schreiben der Cython-Funktion
Wie man nüchtern mit Pandas schreibt
Hinweise zum Schreiben von require.txt
Hinweise zum Ausführen von Cython unter OSX
Wie schreibe ich diesen Prozess in Perl?
Wie schreibe ich Ruby to_s in Python
So schreiben Sie einen Komponententest für den URL-Abruf in GAE / P.
Verwendung von Python Kivy ④ ~ Ausführung unter Android ~
So schreiben Sie in Error Repoting in Python auf GAE
Wie schreibe ich ein benanntes Tupeldokument im Jahr 2020?
[Go] So schreiben oder rufen Sie eine Funktion auf
Wie man eine öffentliche Funktion in Pytest verspottet
[Linux] Unterschied in den Zeitinformationen in Abhängigkeit von der Uhr-ID der Funktion clock_gettime ()
20. Offline-Echtzeit So schreiben Sie Probleme in Python
So schreiben Sie in Python die Verkettung von Zeichenfolgen in mehrere Zeilen
So definieren Sie Decorator und Decomaker mit einer Funktion
So erlauben Sie Nologin-Benutzern, sich unter Linux anzumelden
Unterschied in den Ergebnissen abhängig vom Argument von multiprocess.Process
So schreiben Sie eine benutzerdefinierte Validierung in Django REST Framework
Unterschied in sys.path abhängig davon, wie Python gestartet wird (v3.8.2)
Verwendung von VS-Code in einer venv-Umgebung mit Windows
[Python] Wie schreibe ich eine if-Anweisung in einen Satz?
Hinweise zum Laden einer virtuellen Umgebung mit PyCharm
Wie man aus einer Wahrscheinlichkeitsdichtefunktion in Python tastet
Zusammenfassung zum Schreiben von in gRPC verwendeten .proto-Dateien
Hinweise zur Verwendung von Marshmallow in der Schemabibliothek
[ROS] So schreiben Sie Publisher und Subscriber auf einen Knoten
Covector, um in Funktion zu denken
XPath-Grundlagen (2) - So schreiben Sie XPath
So rufen Sie eine Funktion auf
So registrieren Sie sich bei pypi
Wie man in Python entwickelt
[sh] Das Speichern der Befehlsausführung führt zu Variablen
So installieren Sie OpenCV in Cloud9 und führen es in Python aus
Repeated @ app.callback in Dash So schreiben Sie Input und State ordentlich
So schreiben Sie Code für den Zugriff auf Python dashDB auf Bluemix oder lokal
Das 15. Offline-Echtzeit-Schreiben eines Referenzproblems in Python
So schreiben Sie offline in Echtzeit Lösen von E04-Problemen mit Python
So stellen Sie eine Django-App in nur 5 Minuten für Heroku bereit
Das 14. Referenzproblem beim Schreiben in Echtzeit in Python
Funktion zum Öffnen einer Datei in Python3 (Unterschied zwischen open und codecs.open und Geschwindigkeitsvergleich)
Verwendung der in .mako (.html) direkt in mako definierten Renderfunktion
Das 18. Offline-Echtzeit-Schreiben eines Referenzproblems in Python
[Python] Wie man PCA mit Python macht
Umgang mit Sitzungen in SQLAlchemy
Verwendung der Zip-Funktion
So installieren Sie mysql-connector-python auf einem Mac
Verwendung von Klassen in Theano
So sammeln Sie Bilder in Python
Verwendung von Dataiku unter Windows
Wiederverwendung von Flaschen Wie schreibe ich HTML?
So aktualisieren Sie Spyder in Anaconda
Verwendung von SQLite in Python
Hinweise zur Verwendung von Pywinauto
Schreiben Sie die AWS Lambda-Funktion in Python
So installieren Sie das Graph-Tool unter macOS
So installieren Sie VMware-Tools unter Linux
Messen Sie die Ausführungszeit von Funktionen in Python