好的,这是我下面的第一个 Cython 程序,为 future 欧式期权定价的代码(Black Scholes 没有股息)。它在 10M 选项上的运行时间为 3.5 秒,而我在下面使用直接 numpy Python 3.25 发布的代码。任何人都可以指出为什么我的 Cython 代码较慢 - 就像因为我使用了循环而不是对调用进行矢量化(不确定在 C 中如何做到这一点,但生成的 cython 代码似乎将其矢量化)。我可以在这个循环中使用 nogil
和 openmp
吗,即使变量是从 numpy 数组传入的? Cython 示例中发布的简单示例无法在 http://docs.cython.org/src/userguide/parallelism.html#module-cython.parallel 循环中使用 cython.parallel
prange
正确编译。非常感谢反馈,为一个有点开放的问题道歉 - 其他人可以在这里自由地使用这段代码作为起点,因为它已经比我在 C 和 Python 中看到的其他在线分析工作更快。这里是:
保存为 CyBlack.pyx
文件进行编译(注意所有输入都是 float64
,除了 Black_callput
是 int64
,1 表示调用,-1 表示放置)。编译后, 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
这是
setup.py
以便其他人可以构建此类型:python setup.py build_ext --inplace
使用 VS2015 构建,适用于 Python 3.5 64 位 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)
好的,这是我非常快速的 numpy Python 代码:
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))
最佳答案
我在你的 cython 代码中的函数之前添加了以下几行,我从 Cython 得到的结果比 Python 2.7 快
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
我的 10M 积分结果
%timeit PyBlack(BlackPnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput)
1 loops, best of 3: 3.49 s per loop
和
%timeit CyBlack(BlackPnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput)
1 loops, best of 3: 2.12 s per loop
编辑
CyBlack.pyx
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 fabs(double)
cdef double a1 = 0.254829592
cdef double a2 = -0.284496736
cdef double a3 = 1.421413741
cdef double a4 = -1.453152027
cdef double a5 = 1.061405429
cdef double p = 0.3275911
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef inline double erf(double x):
cdef int sign = 1
if (x < 0):
sign = -1
x = fabs(x)
cdef double t = 1.0/(1.0 + p*x)
cdef double y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*exp(-x*x)
return sign*y
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef double std_norm_cdf(double x):
return 0.5*(1+erf(x/sqrt(2.0)))
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
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
setup.py
try:
from setuptools import setup
from setuptools import Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
ext_modules = [Extension("CyBlack",["CyBlack.pyx"])]
setup(
name= 'Generic model class',
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include()],
ext_modules = ext_modules)
关于python - Cython 程序比普通 Python 慢(10M 选项 3.5s 与 3.25s Black Scholes)-我错过了什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37170077/