11

私は、引数との両方の書き込みなどのメモリへのポインタを取り、そのメモリに読み込む機能fooありますcython共有メモリ - ブロック

cdef void foo (double *data): 
    data[some_index_int] = some_value_double 
    do_something_dependent_on (data) 

私は同じようdataに割り当てていますがso:

cdef int N = some_int 
cdef double *data = <double*> malloc (N * sizeof (double)) 

cdef int i 
for i in cython.parallel.prange (N, nogil=True): 
    foo (data) 

readout (data) 

私の質問は今どうなっているのですか?私の推測では、dataによって指し示されたメモリは、すべてのスレッドによって共有され、同時に、関数fooの中で読み書きされます。これは、以前に設定されたデータ値(foo)に頼ることができないので、すべての結果を混乱させるでしょうか?私の推測は正しいのでしょうか、またはcythonコンパイラに実装された魔法の安全ベルトがありますか?

ありがとうございます。

答えて

2

私は、読み取りまたは書き込みの同期化を行わないと、dataのスレッドは、スレッドがメモリ位置を読み書きし、互いの変更を上書きすると仮定します。あなたは何らかの種類の同期なしで一貫した結果を得ることはありません。

ドキュメント(http://docs.cython.org/src/userguide/parallelism.html)は、OpenMP(デフォルトバックエンド)がスレッドローカルを自動的に作成することを示唆しているようです。

+0

** NB:**私は自分のシステムでOpenMPを持っていないので、これを簡単にテストすることはできません。 –

8

良い方法は、メイン配列をスレッドの外側に配置することです。次に、スレッドによって計算されるべきメイン配列の部分へのポインタを各スレッドに渡します。ここで

c = a*b 

平行度がaの行の上に実装されている:

次の例では、(2-Dアレイのdotに類似)行列乗算の実装です。異なるスレッドが同じ配列を共有できるように、ポインタがmultiply関数にどのように渡されるかを確認してください。 aの行数がCOLSのその後数よりも小さかった場合

finished dot: 0.601547366526 s 
finished mydot: 2.834147917 s 
Passed test: True 

import time 

import numpy as np 

import _stack 

a = np.random.random((10000,500)) 
b = np.random.random((500,2000)) 

t = time.clock() 
c = np.dot(a, b) 
print('finished dot: {} s'.format(time.clock()-t)) 

t = time.clock() 
c2 = _stack.mydot(a, b) 
print('finished mydot: {} s'.format(time.clock()-t)) 

print 'Passed test:', np.allclose(c, c2) 

私のコンピュータ上でそれが与える:

import numpy as np 
cimport numpy as np 
import cython 
from cython.parallel import prange 

ctypedef np.double_t cDOUBLE 
DOUBLE = np.float64 


def mydot(np.ndarray[cDOUBLE, ndim=2] a, np.ndarray[cDOUBLE, ndim=2] b): 
    cdef np.ndarray[cDOUBLE, ndim=2] c 
    cdef int i, M, N, K 

    c = np.zeros((a.shape[0], b.shape[1]), dtype=DOUBLE) 
    M = a.shape[0] 
    N = a.shape[1] 
    K = b.shape[1] 

    for i in prange(M, nogil=True): 
     multiply(&a[i,0], &b[0,0], &c[i,0], N, K) 

    return c 


@cython.wraparound(False) 
@cython.boundscheck(False) 
@cython.nonecheck(False) 
cdef void multiply(double *a, double *b, double *c, int N, int K) nogil: 
    cdef int j, k 
    for j in range(N): 
     for k in range(K): 
      c[k] += a[j]*b[k+j*K] 

は、このスクリプトを使用することができます確認するにはまたはbmydotの列の数が悪くなり、どの次元を並列化するかをよりよく確認する必要があります。