10

TLDR: cython では、なぜ (またはいつ?) numpy 配列を反復する方が python リストを反復するよりも高速ですか?

一般的に: 私は以前に Cython を使用したことがあり、単純な python impl よりも大幅に高速化することができました。

sum() 関数の次の 3 つの実装を検討してください。それらは「cy」と呼ばれるcythonファイルに存在します(明らかに、np.sum()がありますが、それは私の主張の範囲外です..)

素朴なパイソン:

def sum_naive(A):
   s = 0
   for a in A:
       s += a
   return s

Pythonリストを期待する関数を持つCython:

def sum_list(A):
    cdef unsigned long s = 0
    for a in A:
        s += a
    return s

numpy 配列を期待する関数を持つ Cython。

def sum_np(np.ndarray[np.int64_t, ndim=1] A):
    cdef unsigned long s = 0
    for a in A:
        s += a
    return s

実行時間に関しては、sum_np < sum_list < sum_naiveと予想されますが、次のスクリプトは逆を示しています (完全を期すために、 np.sum() を追加しました)。

N = 1000000
v_np = np.array(range(N))
v_list = range(N)

%timeit cy.sum_naive(v_list)
%timeit cy.sum_naive(v_np)
%timeit cy.sum_list(v_list)
%timeit cy.sum_np(v_np)
%timeit v_np.sum()

結果:

In [18]: %timeit cyMatching.sum_naive(v_list)
100 loops, best of 3: 18.7 ms per loop

In [19]: %timeit cyMatching.sum_naive(v_np)
1 loops, best of 3: 389 ms per loop

In [20]: %timeit cyMatching.sum_list(v_list)
10 loops, best of 3: 82.9 ms per loop

In [21]: %timeit cyMatching.sum_np(v_np)
1 loops, best of 3: 1.14 s per loop

In [22]: %timeit v_np.sum()
1000 loops, best of 3: 659 us per loop

どうしたの?cython+numpy が遅いのはなぜですか?

PS #cython: boundscheck=False #cython : wraparound=False
を使用します

4

2 に答える 2

11

これを cython で実装するより良い方法があります。これはnp.sum、任意の配列を処理するときに numpy が通常行わなければならない型チェックやその他のことを回避するため、少なくとも私のマシンではうまくいきます。

#cython.wraparound=False
#cython.boundscheck=False
cimport numpy as np

def sum_np(np.ndarray[np.int64_t, ndim=1] A):
    cdef unsigned long s = 0
    for a in A:
        s += a
    return s

def sum_np2(np.int64_t[::1] A):
    cdef:
        unsigned long s = 0
        size_t k

    for k in range(A.shape[0]):
        s += A[k]

    return s

そしてタイミング:

N = 1000000
v_np = np.array(range(N))
v_list = range(N)

%timeit sum(v_list)
%timeit sum_naive(v_list)
%timeit np.sum(v_np)
%timeit sum_np(v_np)
%timeit sum_np2(v_np)
10 loops, best of 3: 19.5 ms per loop
10 loops, best of 3: 64.9 ms per loop
1000 loops, best of 3: 1.62 ms per loop
1 loops, best of 3: 1.7 s per loop
1000 loops, best of 3: 1.42 ms per loop

Python スタイルを介して numpy 配列を反復処理するのではなく、Python API に依存するのではなく、純粋な C に変換できるように、インデックスを使用して要素にアクセスします。

于 2013-09-21T22:43:22.773 に答える
3

aは型指定されていないため、Python から C 型への変換、およびその逆の変換が多数発生します。これらは遅くなる可能性があります。

JoshAdel は、反復するのではなく、範囲を反復する必要があることを正しく指摘しました。Cython はインデックスを高速な C に変換します。


を使用cython -a myfile.pyxすると、これらの種類のものが強調表示されます。速度を最大にするために、すべてのループ ロジックを白にする必要があります。

PS:np.ndarray[np.int64_t, ndim=1]は時代遅れであり、より高速で一般的なlong[:].

于 2013-09-21T22:07:47.260 に答える