本文介绍了Cython程序比普通的Python慢​​(10M选项3.5s vs 3.25s Black Scholes)-我缺少什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,这是我下面的第一个Cython程序,该代码用于对期货的欧洲期权定价(无股息的Black Scholes).它在10M选项上的运行时间为3.5秒,而我下面用纯正的numpy Python 3.25s发布的代码则与此相反.谁能指出我的Cython代码为什么变慢-就像是因为我使用循环而不是对调用进行向量化(不确定在C中如何做到这一点,但是生成的cython代码似乎对它进行了向量化).即使变量是从numpy数组传入的,我也可以在该循环周围使用nogilopenmp吗?在Cython的示例上发布的简单示例无法在循环 http://docs.cython.org/src/userguide/parallelism.html#module-cython.parallel .反馈受到高度赞赏,对于一个开放性问题,我们深表歉意-其他人可以在这里自由地使用此代码作为起点,因为它的运行速度比我在C和Python中看到的其他在线概述的工作要快.在这里:

Okay here's my first Cython program below, the code to price European options on futures (Black Scholes without a dividend). It runs in 3.5s on 10M options, versus the code I posted below with straight numpy Python 3.25s. Can anyone point out why my Cython code is slower - like because I used a loop instead of vectorizing the call (not sure in C how to do that, the generated cython code appears to vectorize it though). Can I use nogil and openmp around this loop, even though the variables are passed in from numpy arrays? The simple examples posted on Cython's examples don't compile correctly with cython.parallel prange on the loop http://docs.cython.org/src/userguide/parallelism.html#module-cython.parallel. Feedback greatly appreciated, apologies for a somewhat open-ended question - others can use this code freely here as a starting point since it already works faster than other work profiled online that I've seen, in C and Python. Here it is:

另存为CyBlack.pyx文件进行编译(请注意,所有输入均为float64,除了Black_callputint64,1为看涨期权,-1为看跌期权).编译后,from CyBlack.CyBlack import CyBlack:

Save as CyBlack.pyx file to compile (note all inputs are float64 except Black_callput which is int64, 1 for a call, -1 for a put). After compiling, 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 64bit Windows.

Here is the setup.py so others can build this typing: python setup.py build_ext --inplace built with VS2015 for Python 3.5 64bit 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唯一代码:

Okay and here is my very fast numpy Python only 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))

推荐答案

我在cython代码中的函数之前添加了以下几行,并且从Cython获得的结果比Python 2.7更快

I added the following lines before your functions in your cython code and I got a faster result from Cython than Python 2.7

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)

我的结果获得1千万分

%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)

这篇关于Cython程序比普通的Python慢​​(10M选项3.5s vs 3.25s Black Scholes)-我缺少什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-31 03:42