10

メモリ ビューが便利で高速であることがわかったので、cython で NumPy 配列を作成することを避け、指定された配列のビューを操作するようにしています。ただし、既存の配列を変更するのではなく、新しい配列を作成することが避けられない場合があります。上位の関数ではこれは目立ちませんが、頻繁に呼び出されるサブルーチンでは目立ちます。次の関数を検討してください

#@cython.profile(False)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
cdef double [:] vec_eq(double [:] v1, int [:] v2, int cond):
    ''' Function output corresponds to v1[v2 == cond]'''
    cdef unsigned int n = v1.shape[0]
    cdef unsigned int n_ = 0
    # Size of array to create
    cdef size_t i
    for i in range(n):
        if v2[i] == cond:
            n_ += 1
    # Create array for selection
    cdef double [:] s = np.empty(n_, dtype=np_float) # Slow line
    # Copy selection to new array
    n_ = 0
    for i in range(n):
        if v2[i] == cond:
            s[n_] = v1[i]
            n_ += 1
    return s

プロファイリングは、ここで得られる速度があることを教えてくれます。私 ができることは、関数を適応させることです。たとえば、このベクトルの平均が計算されたり、合計が計算されたりすることがあります。だから、合計したり平均を取ったりするために、それを書き直すことができました。しかし、サイズを動的に定義して、オーバーヘッドがほとんどないメモリビューを直接作成する方法はありませんか。最初に etc を使用してac バッファーを作成し、関数の最後でバッファーをビューに変換し、ポインターとストライドなどを渡すようなもの..

malloc

編集1: 単純なケースの場合、たとえばこのように関数を適応させることは、受け入れられるアプローチです。引数と合計/平均を取るだけを追加しました。この方法では、配列を作成する必要がなく、関数 malloc 内で簡単に処理できます。これ以上は速くなりませんよね?

# ...
cdef double vec_eq(double [:] v1, int [:] v2, int cond, opt=0):
    # additional option argument
    ''' Function output corresponds to v1[v2 == cond].sum() / .mean()'''
    cdef unsigned int n = v1.shape[0]
    cdef int n_ = 0
    # Size of array to create
    cdef Py_ssize_t i
    for i in prange(n, nogil=True):
        if v2[i] == cond:
            n_ += 1
    # Create array for selection
    cdef double s = 0
    cdef double * v3 = <double *> malloc(sizeof(double) * n_)
    if v3 == NULL:
        abort()
    # Copy selection to new array
    n_ = 0
    for i in range(n):
        if v2[i] == cond:
            v3[n_] = v1[i]
            n_ += 1
    # Do further computation here, according to option
    # Option 0 for the sum
    if opt == 0:
        for i in prange(n_, nogil=True):
            s += v3[i]
        free(v3)
        return s
    # Option 1 for the mean
    else:
        for i in prange(n_, nogil=True):
            s += v3[i]
        free(v3)
        return s / n_
    # Since in the end there is always only a single double value, 
    # the memory can be freed right here
4

3 に答える 3

3

cpython配列の扱い方がわからなかったので、 fabrizioMが提案したように、自作の「メモリビュー」で最終的に解決しました。これがうまくいくとは思わなかったでしょう。タイトなループで新しい np.array を作成するのはかなりコストがかかるため、これにより大幅なスピードアップが得られました。必要なのは 1 次元配列だけなので、ストライドを気にする必要さえありませんでした。しかし、より高次元の配列の場合でも、これはうまくいくと思います。

cdef class Vector:
    cdef double *data
    cdef public int n_ax0

    def __init__(Vector self, int n_ax0):
        self.data = <double*> malloc (sizeof(double) * n_ax0)
        self.n_ax0 = n_ax0

    def __dealloc__(Vector self):
        free(self.data)

...
#@cython.profile(False)
@cython.boundscheck(False)
cdef Vector my_vec_func(double [:, ::1] a, int [:] v, int cond, int opt):
    # function returning a Vector, which can be hopefully freed by del Vector
    cdef int vecsize
    cdef size_t i
    # defs..
    # more stuff...
    vecsize = n
    cdef Vector v = Vector(vecsize)

    for i in range(vecsize):
        # computation
        v[i] = ...

    return v

...
vec = my_vec_func(...
ptr_to_data = vec.data
length_of_vec = vec.n_ax0
于 2014-01-11T21:36:46.000 に答える
0

Cython メーリング リストの次のスレッドは、おそらく興味深いものになるでしょう。

https://groups.google.com/forum/#!topic/cython-users/CwtU_jYADgM

パフォーマンスがそれほど問題にならない別のレベルで強制される関数からメモリビューを返すことに問題がない場合、適切なオプションがいくつか提示されているようです。

于 2014-01-08T20:32:51.597 に答える