Cython没有速度提升

Ran*_*l J 24 python optimization numpy cython

我试图定义一个包含内部循环的函数来模拟积分.

问题是速度.在我的机器上评估该功能一次最多可能需要30秒.由于我的最终目标是最小化这个功能,一些额外的速度会很好.

因此,我试图让Cython工作,但我必须犯一个严重的错误(可能很多人!).在Cython文档之后,我尝试输入我的变量.这样做之后,代码就像纯Python一样慢.这看起来很奇怪.

这是我的代码:

import numpy as np 
cimport cython
cimport numpy as np
import minuit

data = np.genfromtxt('q6data.csv', usecols = np.arange(1, 24, 1), delimiter = ',')  

cdef int ns    = 1000                 # Number of simulation draws
cdef int K     = 5                    # Number of observed characteristics, including            constant
cdef int J     = len(data[:,1])       # Number of products, including outside
cdef double tol   = 0.0001            # Inner GMM loop tolerance
nu = np.random.normal(0, 1, (6, ns))  # ns random deviates

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


def S(np.ndarray[double, ndim=1] delta, double s1, double s2, double s3, double s4,  double s5, double a):
    """Computes the simulated integrals, one for each good.
    Parameters: delta is an array of length J containing mean product specific utility levels
    Returns: Numpy array with length J."""
    cdef np.ndarray[double, ndim=2] mu_ij = np.dot((data[:,2:7]*np.array([s1, s2, s3, s4, s5])), nu[1:K+1,:])
    cdef np.ndarray[double, ndim=2] mu_y  = a * np.log(np.exp(data[:,21].reshape(J,1) +  data[:,22].reshape(J,1)*nu[0,:].reshape(1, ns)) - data[:,7].reshape(J,1))
    cdef np.ndarray[double, ndim=2] V = delta.reshape(J,1) + mu_ij + mu_y
    cdef np.ndarray[double, ndim=2] exp_vi = np.exp(V)
    cdef np.ndarray[double, ndim=2] P_i = (1.0 / np.sum(exp_vi[np.where(data[:,1] == 71)], 0)) * exp_vi[np.where(data[:,1] == 71)] 
    cdef int yrs = 19
    cdef int yr
    for yr in xrange(yrs):
        P_yr = (1.0 / np.sum(exp_vi[np.where(data[:,1]== (yr + 72))], 0)) *   exp_vi[np.where(data[:,1] == (yr + 72))]
        P_i  = np.concatenate((P_i, P_yr)) 
    cdef np.ndarray[double, ndim=1] S = np.zeros(dtype = "d", shape = J)
    cdef int j
    for j in xrange(ns):
        S += P_i[:,j]
    return (1.0 / ns) * S

def d_infty(np.ndarray[double, ndim=1] x, np.ndarray[double, ndim=1] y):
    """Sup norm."""
    return np.max(np.abs(x - y)) 

def T(np.ndarray[double, ndim=1] delta_exp, double s1, double s2, double s3, double s4,  double s5, double a):
    """The contraction operator.  This function takes the parameters and the exponential
    of the starting value of delta and returns the fixed point.""" 
    cdef int iter = 0
    cdef int maxiter = 200
    cdef int i
    for i in xrange(maxiter): 
        delta1_exp = delta_exp * (data[:, 8] / S(np.log(delta_exp), s1, s2, s3, s4, s5, a))                    
        print i
        if d_infty(delta_exp, delta1_exp) < tol:                                       
            break
        delta_exp = delta1_exp
    return np.log(delta1_exp)


def Q(double s1, double s2, double s3, double s4, double s5, double a):
    """GMM objective function."""  
    cdef np.ndarray[double, ndim=1] delta0_exp = np.exp(data[:,10])                                                     
    cdef np.ndarray[double, ndim=1] delta1 = T(delta0_exp, s1, s2, s3, s4, s5, a)
    delta1[np.where(data[:,10]==0)] = np.zeros(len(np.where(data[:,10]==0)))            
    cdef np.ndarray[double, ndim=1] xi =  delta1 - (np.dot(data[:,2:7],   np.linalg.lstsq(data[:,2:7], delta1)[0]))   
    cdef np.ndarray[double, ndim=2] g_J = xi.reshape(J,1) * data[:,11:21]
    cdef np.ndarray[double, ndim=1] G_J = (1.0 / J) * np.sum(g_J, 0) 
    return np.sqrt(np.dot(G_J, G_J))
Run Code Online (Sandbox Code Playgroud)

我已经分析了代码,它似乎是功能S,即整体模拟器,它正在扼杀性能.无论如何,我希望通过键入我的变量至少可以获得一些速度提升.由于它没有产生任何收益,我被引导相信我犯了一些根本性的错误.

是否有人在Cython代码中看到可能导致此结果的明显错误?

哦,因为我对编程很陌生,所以肯定会有很多不好的风格和减慢代码的事情.如果你有时间,请随时让我直截了当.

Jon*_*ley 29

Cython可以生成一个html文件来帮助解决这个问题:

cython -a MODULE.py
Run Code Online (Sandbox Code Playgroud)

这显示每行源代码通过各种黄色阴影着色为白色.黄色越深,仍然在该行上执行的动态Python行为越多.对于包含黄色的每一行,您需要添加更多静态类型声明.

当我这样做时,我喜欢将我遇到的部分源代码分成许多单独的行,每个表达式或运算符一行,以获得最精细的视图.

如果没有这个,很容易忽略变量,函数调用或运算符的一些静态类型声明.(例如索引运算符x [y]仍然是一个完全动态的Python操作,除非你另外声明)

  • 我喜欢为这样的小随机命令创建一个小Makefile.然后'make cython'将执行cythoning(像往常一样使用setup.py),然后运行上面的命令.在Windows上,我喜欢安装Cygwin并将bin目录放在我的Path上,所以我可以访问'make'和其他unix-y命令,即使是在DOS提示符下也是如此. (3认同)

Lup*_*uch 16

Cython不提供自动性能提升,您必须知道其内部并检查生成的C代码.

特别是如果你想改善循环性能,你必须避免在其中调用Python函数,在这种情况下你碰巧做了很多(所有np.调用都是Python调用,切片,可能还有其他东西).

请参阅本页面以获取有关用Cython性能优化(优化时-a开关确实是方便)和一般准则这一项优化numpy的代码时的具体情况.


Eri*_*got 11

通过使用Numpy的更多功能,您肯定可以加快代码速度.

例如:

cdef np.ndarray[double, ndim=1] S = np.zeros(dtype = "d", shape = J)
cdef int j
for j in xrange(ns):
    S += P_i[:,j]
Run Code Online (Sandbox Code Playgroud)

会更加快速和清晰

S = P_i.sum(axis=1)
Run Code Online (Sandbox Code Playgroud)

您还重复一些计算,因此需要的时间比必要时间长两倍.例如

np.where(data[:,1]==(yr + 72))
Run Code Online (Sandbox Code Playgroud)

只能计算一次并存储在您可以重用的变量中.

您还执行了大量的整形和切片:从一开始就可以让您的变量采用更简单的格式.如果可能,您的代码将更加清晰,优化可能会更加明显.


Jon*_*ley 6

分析器会帮助您找出哪个部分很慢吗?我喜欢使用标准库分析器运行程序:

python -O -m cProfile -o profile.out MYAPP.py
Run Code Online (Sandbox Code Playgroud)

然后在'RunSnakeRun'GUI中查看输出:

runsnake profile.out
Run Code Online (Sandbox Code Playgroud)

可以从这里安装RunSnakeRun:http://www.vrplumber.com/programming/runsnakerun/

RunSnakeRun截图


ato*_*zer -7

“根本性错误”是您期望 Python 在长循环中具有良好的性能。它是一种解释性语言,在实现和 ctyping 之间切换对此没有任何作用。有一些用于快速计算的数值 Python 库,大部分是用 C 编写的。例如,如果您已经用于numpy数组,为什么不进一步用于scipy高级数学呢?它将提高可读性速度。

  • Cython 的重点在于它是专门为加速 python 的性能而设计的,在许多情况下可能实现类似 C 的性能。所以这个问题不存在“根本性错误”。 (6认同)
  • 经过反思,并不是所有的性能特征——我错了。但其中一些!:-) (3认同)
  • Cython 不是 Python 的实现,它是一种编译为 C 的类似 Python 的语言。此外,Python *不是*一种解释性语言。 (2认同)
  • 说 Python 不被解释虽然在技术上是正确的,但在没有给出上下文的情况下会产生误导。当您运行 Python 程序时,“解释器”会将源代码转换为字节码,然后执行。从黑盒的角度来看,它的行为与传统解释器完全相同,包括性能特征。 (2认同)