2016-06-26 10 views
2

Ich habe Cython-Code, den ich versuche, als DLL zu kompilieren, so dass ich es aus anderen Sprachen aufrufen kann. Die seltsame Sache ist, dass die Verwendung von STL-Vektoren anstelle von NumPy MemoryViews ich 12x niedrigere Leistung bekomme. Der Versuch, OpenMP mit Cythons prange zu verwenden, scheint auch nicht zu funktionieren (ich bekomme 100% Auslastung für alle 4 Threads mit Speicheransichten und vielleicht 50% max mit STL-Vektoren). Hat jemand irgendwelche Gedanken darüber, wie man die STL-Version umgestalten kann, um vergleichbar zu sein? Der Cython-Profiler zeigt nur die cimport cython und cpdef-Anweisungen für die Interaktion mit Python ... könnte es sein, dass, wenn sie aus C++ aufgerufen werden, nur umbenennen sie als cdef würde Dinge verbessern? Oder muss ich Intel MKL-Vektoren wie in den Beispielen hier https://software.intel.com/en-us/node/531898 verwenden, die mit einfachen Option Formeln sind wirklich keine große Sache, um neu zu schreiben ...? Ich bin wirklich so unerfahren in C++ würde ich im Internet recherchieren muß nur ein C++ erstellen nur Testskript ... Code-Segment unter:Cython mit STL-Vektor & kein NumPy zum Anrufen von anderen Apps, was fehlt?

cimport cython 
from libcpp.vector cimport vector 

cdef extern from "math.h": 
    double exp(double) 
    double sqrt(double) 
    double log(double) 
    double erf(double) 

cdef inline double std_norm_cdf(double x): 
    return 0.5*(1+erf(x/sqrt(2.0))) 

cpdef CyBlackP(vector[double] Black_PnL, vector[double] Black_S, vector[double] Black_Texpiry, vector[double] Black_strike, vector[double] Black_volatility, vector[double] Black_IR, vector[int] Black_callput): 
    cdef int i, N 
    N = Black_PnL.size() 
    cdef double d1, d2 

    for i in range(N): 
     d1 = (log(Black_S[i]/Black_strike[i]) + Black_Texpiry[i] * (Black_volatility[i] *Black_volatility[i])/2)/(Black_volatility[i] * sqrt(Black_Texpiry[i])) 
     d2 = d1 - Black_volatility[i] * sqrt(Black_Texpiry[i]) 
     Black_PnL[i] = exp(-Black_IR[i] * Black_Texpiry[i]) * (Black_callput[i] * Black_S[i] * std_norm_cdf(Black_callput[i] * d1) - Black_callput[i] * Black_strike[i] * std_norm_cdf(Black_callput[i] * d2)) 

    return Black_PnL 

Für andere da draußen dieser Code oben gebunden ist aus vollständig Seien Sie mit dem schwarzen Modell genau, wenn Sie es verwenden möchten.

+0

Wie rufst du das an? (also diese Version, nicht die numpy) – chrisb

+0

Nur als eine Randnotiz sollte es möglich sein, Funktionsdefinitionen wie 'von libc.math cimport exp, sqrt, log, erf' zu importieren –

+0

@chrisb ich habe es nicht aufgerufen C++ versuchte gerade noch von Python für die Timings mit NumPy-Arrays: Black_PnL = np.zeros (10000000) Black_S = np.random.randint (200, 10000, 10000000) * 0,01 Black_volatility = np.random.rand (10000000) * 1.2 Black_Texpiry = np.abs (np.random.randint (1.500,10000000) * 0,01) Black_strike = np.random.uniform (0,2,10000000) * Black_S Black_IR = np.random.rand (10000000) * 0,1 Black_callput = np.sign (np.random.randn (10000000)) Black_callput = Black_callput.type (np.int) % zeit cyBlackP (Black_PnL, Black_S, Black_Texp Iry, Black_strike, Black_volatility, Black_IR, Black_callput) – Matt

Antwort

3

Dies ist langsam, weil numpy Arrays und C++ Vektoren nicht austauschbar sind - basierend auf dem Dokonotiz here scheint es, als ob das numpy Array in einen neuen Vektor iteriert/kopiert wird.

Betrachten wir zum Beispiel ein paar Funktionen, die nichts tun:

# ext.pyx 
cpdef pass_vec(vector[double] v): 
    return 0.0 

cpdef pass_arr(double[::1] a): 
    return 0.0 

die Timings unter Highlight, wie viel Aufwand es ist. Beachten Sie, dass Ihre Funktion schnell ist, wenn sie mit einem C++ - Vektor als Argument aufgerufen wird, nur nicht mit einem übergebenen numpy-Array.

In [1]: import ext 

In [2]: a = np.zeros(1000000) 

In [4]: %timeit ext.pass_arr(a) 
1000000 loops, best of 3: 808 ns per loop 

In [5]: %timeit ext.pass_vec(a) 
10 loops, best of 3: 63 ms per loop 
+0

Okay, gerade gesehen, dass Ihre Antwort die 'enthält ext.pyx'-Funktion, versucht, etwas vergleichbar mit dem zu überliefern, was ich habe, aber ein wenig verwirrt zu den obigen Zeitpunkten, da 1 1M macht und der andere 10 Loops ...Ich sehe 63ms = 63M ns und versuche herauszufinden, wie ich den C++ - Vektor an die Funktion übergeben würde, anstatt ein NumPy-Array zu übergeben. Aus dem, was ich verstehe, die viel schnellere "pass_arr" -Funktion ist mit einem 'MemoryView' und die andere ist nicht, noch Ihre Antwort impliziert, dass die Übergabe eines C++ Vektor wäre schnell, aber ich sehe nicht den Funktionsaufruf unterstützt es ??? Haben Sie eine 'pass_vec' Illustration? – Matt

+0

'timeit' meldet das Timing pro Schleife, es laufen nur mehr Schleifen wenn etwas schnell ist. Um einen C++ - Vektor zu übergeben, müssten Sie die Funktion aus C++ aufrufen, jeder Python-Typ muss eine Art Konvertierungsschicht durchlaufen. – chrisb

+0

Dank lassen Sie mich das eine Chance geben .. – Matt