Okay, hier ist mein erstes Cython-Programm, der Preiscode für europäische Optionen auf Futures (Black Scholes ohne Dividende). Es läuft in 3.5s auf 10M Optionen, gegen den Code, den ich unten mit geraden numpy Python 3.25s postete. Kann jemand darauf hinweisen, warum mein Cython-Code langsamer ist - etwa weil ich eine Schleife verwendet habe, anstatt den Aufruf zu vektorisieren (nicht sicher in C, wie das zu tun ist, scheint der erzeugte Cython-Code ihn zu vektorisieren). Kann ich nogil
und openmp
um diese Schleife verwenden, obwohl die Variablen von numpy Arrays übergeben werden? Die einfachen Beispiele, die in Cythons Beispielen gepostet werden, werden nicht korrekt mit cython.parallel
prange
auf der Schleife http://docs.cython.org/src/userguide/parallelism.html#module-cython.parallel kompiliert. Feedback sehr geschätzt, Entschuldigungen für eine etwas offene Frage - andere können diesen Code hier frei als Ausgangspunkt verwenden, da es bereits schneller arbeitet als andere Arbeit profiliert online, die ich gesehen habe, in C und Python. Hier ist sie:Cython-Programm ist langsamer als normal Python (10M Optionen 3.5 vs 3.25s Black Scholes) - was vermisse ich?
Speichern als CyBlack.pyx
Datei zu kompilieren (beachten Sie alle Eingänge float64
außer Black_callput
sind die int64
, 1 für einen Anruf ist, -1 für eine Put). Nach dem Kompilieren from CyBlack.CyBlack import CyBlack
:
from numpy cimport ndarray
cimport numpy as np
cimport cython
cdef extern from "math.h":
double exp(double)
double sqrt(double)
double log(double)
double erf(double)
cdef double std_norm_cdf(double x):
return 0.5*(1+erf(x/sqrt(2.0)))
@cython.boundscheck(False)
cpdef CyBlack(ndarray[np.float64_t, ndim=1] BlackPnL, ndarray[np.float64_t, ndim=1] Black_S, ndarray[np.float64_t, ndim=1] Black_Texpiry, ndarray[np.float64_t, ndim=1] Black_strike, ndarray [np.float64_t, ndim=1] Black_volatility, ndarray[np.float64_t, ndim=1] Black_IR, ndarray[np.int64_t, ndim=1] Black_callput):
cdef Py_ssize_t i
cdef Py_ssize_t N = BlackPnL.shape[0]
cdef double d1, d2
for i in range(N):
d1 = ((log(Black_S[i]/Black_strike[i]) + Black_Texpiry[i] * Black_volatility[i] **2/2))/(Black_volatility[i] * sqrt(Black_Texpiry[i]))
d2 = d1 - Black_volatility[i] * sqrt(Black_Texpiry[i])
BlackPnL[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 BlackPnL
Hier ist die setup.py
, damit andere diese Typisierung aufbauen können: python setup.py build_ext --inplace
mit VS2015 gebaut für Python 3.5 64-Bit-Windows-.
from setuptools import setup
from setuptools import Extension
from Cython.Distutils import build_ext
import numpy as np
ext_modules = [Extension("CyBlack",sources=["CyBlack.pyx"],
extra_compile_args=['/Ox', '/openmp', '/favor:INTEL64'],
language='c++')]
setup(
name= 'Generic model class',
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include()],
ext_modules = ext_modules)
Okay, und hier ist mein sehr schnell numpy Python nur Code:
import numpy as np
from scipy.stats import norm
d1=((np.log(Black_S/Black_strike) + Black_Texpiry * Black_volatility **2/2))/(Black_volatility * np.sqrt(Black_Texpiry))
d2=d1 - Black_volatility * np.sqrt(Black_Texpiry)
BlackPnL = np.exp(-Black_IR * Black_Texpiry) * (Black_callput * Black_S * norm.cdf(Black_callput * d1) - Black_callput * Black_strike * norm.cdf(Black_callput * d2))
Sie sollten in der Regel nicht Cython erwarten eine große Beschleunigung bieten über vektorisierte Nummernoperationen. Die anzahlmäßigen Operationen werden bereits in C ausgeführt. – BrenBarn
BrenBarn hat Recht - es sind die * nicht-vektorisierten * Operationen, die Sie am meisten von einem Absturz auf C profitieren. Eine Sache, die Sie * jedoch tun können, um zu sehen, ob es Optimierungen gibt noch nicht erfasst ist, cython mit '-a' auszuführen und den resultierenden HTML-Code in einem Browser zu betrachten. Ein schneller Skim lässt es zum Beispiel so aussehen, als würde es "pow" heißen anstatt quadrieren. – DSM
Macht Sinn, ich habe 'pow' in' ** 'und' sqrt' in '** 0.5' geändert, ich dachte, ich könnte etwas mit' no gil' und 'openmp' machen, um zumindest Multi Threading zu bekommen . Dies ist nur das einfachste Finanzbeispiel, das ein guter Baustein für alle anderen analytischen Optionspreisbibliotheken ist, daher denke ich, dass andere davon profitieren werden, diesen Beitrag zu lesen. – Matt