好的,这是我下面的第一个 Cython 程序,为 future 欧式期权定价的代码(Black Scholes 没有股息)。它在 10M 选项上的运行时间为 3.5 秒,而我在下面使用直接 numpy Python 3.25 发布的代码。任何人都可以指出为什么我的 Cython 代码较慢 - 就像因为我使用了循环而不是对调用进行矢量化(不确定在 C 中如何做到这一点,但生成的 cython 代码似乎将其矢量化)。我可以在这个循环中使用 nogilopenmp 吗,即使变量是从 numpy 数组传入的? Cython 示例中发布的简单示例无法在 http://docs.cython.org/src/userguide/parallelism.html#module-cython.parallel 循环中使用 cython.parallel prange 正确编译。非常感谢反馈,为一个有点开放的问题道歉 - 其他人可以在这里自由地使用这段代码作为起点,因为它已经比我在 C 和 Python 中看到的其他在线分析工作更快。这里是:

保存为 CyBlack.pyx 文件进行编译(注意所有输入都是 float64,除了 Black_callputint64 ,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/

10-12 17:54
查看更多