17

のようなnumpy/scipy数学関数に大きく依存するCythonで計算を行おうとしています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おそらく、numpy C 関数を直接呼び出すのではなく、Python を経由するため、これは非常にコストがかかります。その行を次のように置き換えると:

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. Python のオーバーヘッドなしで C から直接 scipy 関数を呼び出す同様の方法はありますか? scipy の C 関数ライブラリをどのようにインポートできますか?

具体例: Cython のループscipy.stats.*内のスカラー値に対して、scipy または numpy の有用な統計関数 (例: ) の多くを呼び出したいとしますか? forこれらすべての関数を 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 オーバーヘッドを使用してこれらの関数を呼び出すと、非常に遅くなります。

ありがとう。

4

1 に答える 1

3

log on numpy array などの C 関数を適用することはできず、numpy には cython から呼び出すことができる C 関数ライブラリがありません。

numpy 関数は、numpy 配列で呼び出されるように既に最適化されています。非常に特殊なユース ケースがない限り、numpy 関数を C 関数として再実装してもあまりメリットはありません。(numpy の一部の機能が適切に実装されていない可能性があります。シックなケースでは、インポートをパッチとして送信することを検討してください。)しかし、良い点を指摘しています。

# 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 のコードです。python で numpy および scipy 呼び出しを使用して記述されていることがわかります。このコードの C バージョンはありません。「python 経由」で呼び出す必要があります。これが妨げになっている場合は、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
于 2013-04-16T05:22:27.187 に答える