2012-06-25 11 views
11

Ich beginne Cython wegen Leistungsproblemen zu lernen. Dieser spezielle Code ist ein Versuch, einige neue Algorithmen im Transportmodellierungsbereich (für die Planung) zu implementieren.Warum ist dieser Code in Cython langsamer als in Python?

Ich entschied mich, mit einer sehr einfachen Funktion zu beginnen, die ich eine LOT verwenden werde (Hunderte von Millionen Male) und würde definitiv von einer Leistungssteigerung profitieren.

I implementiert diese Funktion auf drei verschiedene Arten und testete sie für den gleichen Parameter (der Einfachheit halber) für 10 Millionen Mal je:

  • Cython Code in einem cython Modul. Laufzeit: 3.35s
  • Python-Code in einem Cython-Modul. Laufzeit: 4.88s
  • Python-Code auf dem Hauptskript. Laufzeit: 2.98s

    Wie Sie sehen können, war der Cython-Code 45% langsamer als der Python-Code in einem Cython-Modul und 64% langsamer als der Code, der im Hauptskript geschrieben wurde. Wie ist das möglich? Wo mache ich einen Fehler?

Der cython Code ist dies: Das

def BPR2(vol, cap, al, be): 
    con=al*pow(vol/cap,be) 
    return con 


def func (float volume, float capacity,float alfa,float beta): 
    cdef float congest 
    congest=alfa*pow(volume/capacity,beta) 
    return congest 

Und das Skript zum Testen ist:

agora=clock() 
for i in range(10000000): 
    q=linkdelay.BPR2(10,5,0.15,4) 

agora=clock()-agora 
print agora 

agora=clock() 
for i in range(10000000): 
    q=linkdelay.func(10,5,0.15,4) 

agora=clock()-agora 
print agora 

agora=clock() 
for i in range(10000000): 
    q=0.15*pow(10/5,4) 

agora=clock()-agora 
print agora 

Ich bin mir dessen bewusst Themen wie transzendenten Funktionen (Power) langsamer ist, aber ich denke nicht, dass es ein Problem sein sollte.

Da es einen Overhead für die Suche nach der Funktion auf dem Funktionsbereich gibt, würde es die Leistung unterstützen, wenn ich ein Array für die Funktion übergeben und ein Array zurück bekommen würde? Kann ich ein Array mit einer in Cython geschriebenen Funktion zurückgeben?

Als Referenz Ich verwende: - Windows 7 64-Bit - Python 2.7.3 64 Bits - Cython 0,16 64 Bits - Fenster Visual Studio 2008

+1

Also, wenn Sie darüber nachdenken, ein Array in die Funktion übergeben, vermutlich können Sie den Code vektorisieren, in welchem ​​Fall haben Sie in Betracht gezogen, zu tun Was versuchst du einfach mit [NumPy] (http://numpy.scipy.org/)? Sicherlich kann die Funktion in Ihrem Beispiel auf Arrays mit NumPy trivial implementiert werden. –

+0

Nun, es ist eine extrem triviale Funktion und Cython muss das 'PyObject *' in einen Float konvertieren und dann zurück, oder? Scheint so viel Aufwand für so eine kleine Funktion. – Voo

+1

Nur um zu verdeutlichen, Ihr Problem besteht darin, dass Sie die meiste Zeit damit verbringen, die Funktion aufzurufen, die durch die Verwendung von Cython nicht verbessert wird. Ich schlage vor, dass Sie Ihre Frage neu formulieren, ohne die Lösung zu beeinträchtigen (Cython). Auf diese Weise werden diejenigen, die zu antworten bereit sind, mehr arbeiten müssen. Ein kleines Beispiel dafür, wie Sie den Code tatsächlich verwenden, wäre nützlich. –

Antwort

1

Diese Funktion könnte als solche optimiert werden (in beide python und cython, ist die Zwischengröße Entfernung schneller):

def func(float volume, float capacity, float alfa,f loat beta): 
    return alfa * pow(volume/capacity, beta) 
+1

Das führt nicht gerade zu der gewünschten Größenordnung Geschwindigkeit erhöht sich ... –

+0

Aber es wird helfen. Probieren Sie dies und dann sehen Sie, wo es die Geschwindigkeit bringt. – C0deH4cker

+5

Nein, wird es nicht. Dies ist die Essenz des Problems mit vorzeitiger Optimierung. Setzen Sie Ihre Bemühungen in algorithmische Verbesserungen. –

3

der Test wurde durchgeführt unter Verwendung:

for i in range(10000000): 
    func(2.7,2.3,2.4,i) 

Hier sind die Ergebnisse:

cdef float func(float v, float c, float a, float b): 
    return a * (v/c) ** b 
#=> 0.85 

cpdef float func(float v, float c, float a, float b): 
    return a * (v/c) ** b 
#=> 0.84 

def func(v,c,a,b): 
    return a * pow(v/c,b) 
#=> 3.41 

cdef float func(float v, float c, float a, float b): 
    return a * pow(v/c, b) 
#=> 2.35 

Für höchste Effizienz müssen Sie die Funktion in C definieren, und den Rückgabetyp statisch machen.

+0

Aus Neugier, werden die letzten 2 besser, wenn Sie 'von libc.math cimport pow'? – mgilson

+0

Ich bekomme jetzt 3,35 bzw. 1,35. Also ja. –

+0

Sicherlich jede Reduzierung der Zeit bei der Festlegung eines statischen Rückgabetyps in einer externen C-Bibliothek wird durch den Aufruf-Overhead in den Schatten gestellt. All Ihre Geschwindigkeitssteigerungen sind nur darauf zurückzuführen, dass die Aufrufe in die Python-Bibliotheken minimiert werden. Außerdem bin ich neugierig, was Cython tut, um den Operator '**' schneller als 'libc.math's' pow' zu machen. Gibt es eine Chance, den ausgegebenen C-Code zu veröffentlichen? –

0

Wenn Cython langsamer ist, liegt das wahrscheinlich an Typumwandlungen, die möglicherweise durch fehlende Typanmerkungen noch verschlimmert werden. Wenn Sie C-Datenstrukturen in Cython verwenden, ist dies in der Regel schneller als die Verwendung von Python-Datenstrukturen in Cython.

Ich habe einen Leistungsvergleich zwischen CPython 2.x (mit und ohne Cython, mit und ohne Psyco), CPython 3.x (mit und ohne Cython), Pypy und Jython.Pypy war bei weitem der schnellste, zumindest für die Mikro-Benchmark untersucht: http://stromberg.dnsalias.org/~strombrg/backshift/documentation/performance/

Verwandte Themen