如何从用Cython直接调用numpy的/ SciPy的C函数,没有Python的调用开销?(How

2019-08-31 06:57发布

我试图在用Cython,在很大程度上依赖于像一些numpy的/ SciPy的数学函数计算numpy.log 。 我注意到,如果我在用Cython一个循环反复调用numpy的/ SciPy的功能外,还有巨大的间接成本,如:

import numpy as np
cimport numpy as np
np.import_array()
cimport cython

def myloop(int num_elts):
   cdef double value = 0
   for n in xrange(num_elts):
     # call numpy function
     value = np.log(2)

这是非常昂贵的,大概是因为np.log经过Python的,而不是直接调用numpy的C函数。 如果我更换符合:

from libc.math cimport log
...
# calling libc function 'log'
value = log(2)

那么它的速度更快。 然而,当我试图通过一个numpy的阵列到libc.math.log:

cdef np.ndarray[long, ndim=1] foo = np.array([1, 2, 3])
log(foo)

它给出了这样的错误:

TypeError: only length-1 arrays can be converted to Python scalars

我的问题是:

  1. 是否可以调用C函数,并将它传递一个numpy的阵列? 或者,可以只对标量值使用,这要求我写一个循环(例如,如果我想将它应用到foo上述阵列。)
  2. 有没有类似的方法来调用由C SciPy的功能的情况下直接Python的开销? 这我怎么能导入SciPy的的C函数库?

具体的例子:假设你要调用许多SciPy的公司或numpy的非常有用的统计功能(如scipy.stats.*上标量值内) for在用Cython循环? 这太疯狂了重新实现在用Cython所有这些功能,所以它们的C型可以被调用。 例如,所有的功能相关的各种统计分布PDF / CDF和采样(例如,参见http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.rv_continuous.pdf.html#scipy。 stats.rv_continuous.pdf和http://www.johndcook.com/distributions_scipy.html )如果你在一个循环中调用与Python开销这些功能,这将是惊人缓慢。

谢谢。

Answer 1:

您不能将C函数,如登录numpy的数组和numpy的没有一个C函数库,你可以用Cython调用。

NumPy的功能已经被优化过上numpy的阵列被调用。 除非你有一个非常独特的使用情况下,你不会看到来自重新实现numpy的功能作为C功能多少好处。 (这有可能是在numpy的某些功能未落实到位,在chich情况考虑提交你的输入情况的补丁。)然而,你带来了一个好点。

# A
from libc.math cimport log
for i in range(N):
    r[i] = log(foo[i])

# B
r = np.log(foo)

# C
for i in range(n):
    r[i] = np.log(foo[i])

在一般情况下,A和B应该有类似的运行时间,但应避免C和会慢得多。

更新

下面是scipy.stats.norm.pdf的代码,你可以看到它是写在numpy的和SciPy的通话蟒蛇。 有了这个代码,不C版,你必须把它称为“通过蟒”。 如果这是什么阻碍了你,你就需要重新植入它在C /用Cython,但首先我会非常仔细地花一些时间剖析代码,看看是否有任何低悬果后首先去了。

def pdf(self,x,*args,**kwds):
    loc,scale=map(kwds.get,['loc','scale'])
    args, loc, scale = self._fix_loc_scale(args, loc, scale)
    x,loc,scale = map(asarray,(x,loc,scale))
    args = tuple(map(asarray,args))
    x = asarray((x-loc)*1.0/scale)
    cond0 = self._argcheck(*args) & (scale > 0)
    cond1 = (scale > 0) & (x >= self.a) & (x <= self.b)
    cond = cond0 & cond1
    output = zeros(shape(cond),'d')
    putmask(output,(1-cond0)+np.isnan(x),self.badvalue)
    if any(cond):
        goodargs = argsreduce(cond, *((x,)+args+(scale,)))
        scale, goodargs = goodargs[-1], goodargs[:-1]
        place(output,cond,self._pdf(*goodargs) / scale)
    if output.ndim == 0:
        return output[()]
    return output


文章来源: How to call numpy/scipy C functions from Cython directly, without Python call overhead?