以下是使用实际多线程在Cython中编写的期货期权的Black(Black Scholes减去股息)期权定价模型,但我无法运行它。 (现在固定,以后再看下面的答案)。我使用Python 3.5与Microsoft Visual Studio 2015编译器。以下是10M选项需要3.5秒的串行版本:Cython program is slower than plain Python (10M options 3.5s vs 3.25s Black Scholes) - what am I missing?
我尝试使用nogil
使其并行,但在编译之后,我无法访问内部函数CyBlackP
。这有几个问题(至少在Windows上)。 1)生成OpenMP代码时的Cython假设您超出v2.0但Microsoft Visual Studio 2015仍然停留在需要签名迭代器的旧版本上。我的解决方法是在第一次尝试构建代码之后,它会出错,然后在Microsoft Visual Studio 2015中打开输出CyBlackP.cpp
文件,搜索size_t __pyx_t_2
(第1430行),然后将其更改为ssize_t __pyx_t_2
,并将下一行从size_t __pyx_t_3
更改为ssize_t __pyx_t_3
以删除已签名/未签名的错误,然后重新编译。 2)你不能直接从NumPy数组进入函数,nogil
仅适用于纯C / C ++函数,所以我有几个辅助函数将NumPy数组输入转换为C ++ vector
格式,将它们传递给C ++函数,然后将返回的vector
转换回NumPy数组。我在这里发布并行代码供其他人使用,我确信那里有人可以弄清楚为什么我无法从Python访问并行函数 - 非并行版本就像这样from CyBlackP.CyBlackP import CyBlackP
被访问了。
代码在这里有关于如何构建的步骤。第一个文件保存为CyBlackP.pyx
[注意这里Python的公开函数是CyBlackP
,它通过辅助函数将NumPy输入数组转换为C向量,然后将C向量传递给C函数CyBlackParallel
,该函数以{{1}运行和OpenMP。然后将结果转换回NumPy数组并从nogil
返回到Python]:
CyBlackP
下一个代码段另存为import numpy as np
cimport cython
from cython.parallel cimport prange
from libcpp.vector cimport vector
cdef extern from "math.h" nogil:
double exp(double)
double log(double)
double erf(double)
double sqrt(double)
cdef double std_norm_cdf(double x) nogil:
return 0.5*(1+erf(x/sqrt(2.0)))
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef CyBlackParallel(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 = Black_PnL.size()
cdef double d1, d2
for i in prange(N, nogil=True, num_threads=4, schedule='static'):
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
cdef vector[double] arrayToVector(np.ndarray[np.float64_t,ndim=1] array):
cdef long size = array.size
cdef vector[double] vec
cdef long i
for i in range(size):
vec.push_back(array[i])
return vec
cdef vector[int] INTarrayToVector(np.ndarray[np.int64_t,ndim=1] array):
cdef long size = array.size
cdef vector[int] vec
cdef long i
for i in range(size):
vec.push_back(array[i])
return vec
cdef np.ndarray[np.float64_t, ndim=1] vectorToArray(vector[double] vec):
cdef np.ndarray[np.float64_t, ndim=1] arr = np.zeros(vec.size())
cdef long i
for i in range(vec.size()):
arr[i] = vec[i]
return arr
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cpdef CyBlackP(ndarray[np.float64_t, ndim=1] PnL, ndarray[np.float64_t, ndim=1] S0, ndarray[np.float64_t, ndim=1] Texpiry, ndarray[np.float64_t, ndim=1] strike, ndarray [np.float64_t, ndim=1] volatility, ndarray[np.float64_t, ndim=1] IR, ndarray[np.int64_t, ndim=1] callput):
cdef vector[double] Black_PnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR
cdef ndarray[np.float64_t, ndim=1] Results
cdef vector[int] Black_callput
Black_PnL = arrayToVector(PnL)
Black_S = arrayToVector(S0)
Black_Texpiry = arrayToVector(Texpiry)
Black_strike = arrayToVector(strike)
Black_volatility = arrayToVector(volatility)
Black_IR = arrayToVector(IR)
Black_callput = INTarrayToVector(callput)
Black_PnL = CyBlackParallel (Black_PnL, Black_S, Black_Texpiry, Black_strike, Black_volatility, Black_IR, Black_callput)
Results = vectorToArray(Black_PnL)
return Results
,供setup.py
使用:
Cython
然后在命令提示符下键入: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("CyBlackP",sources=["CyBlackP.pyx"],
extra_compile_args=['/Ot', '/openmp', '/favor:INTEL64', '/EHsc', '/GA'],
language='c++')]
setup(
name= 'Generic model class',
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include()],
ext_modules = ext_modules)
进行构建。
任何有关获取此功能的帮助都表示赞赏,不确定为什么我在编译后似乎无法找到它。我可以python setup.py build_ext --inplace --compiler=msvc
或import CyBlackP
但我无法使用实际函数来计算选项值。
如果你想测试这个Cython函数,这是一个现实的NumPy测试脚本:
from CyBlackP import *
答案 0 :(得分:0)
好吧,我在Cython生成的CyBlackP.cp35-win_amd64.pyd
文件中找出使用依赖性walker http://www.dependencywalker.com/的错误。它显示找不到2个DLL:msvcp140_app.dll
和vcomp140_app.dll
只是x64版本的MSVC OpenMP,CRT C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\
Microsoft.VC140.OpenMP\vcomp140.dll
和C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC14
0.CRT\msvcp140.dll
已重命名为_app
,并复制到\CyBlackP\
项目目录。我也像这样更新了我的setup.py
,它摆脱了烦人的导入语句(现在只是from CyBlackP import CyBlackP
):
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
import os
module = 'CyBlackP'
ext_modules = [Extension(module, sources=[module + ".pyx"],
extra_compile_args=['/Ot', '/favor:INTEL64', '/EHsc', '/GA', '/openmp'],
language='c++')]
setup(
name = module,
cmdclass = {'build_ext': build_ext},
include_dirs = [np.get_include(), os.path.join(np.get_include(), 'numpy')],
ext_modules = ext_modules)