我有一个函数foo
,它将指向内存的指针作为参数,并写入和读取该内存:
cdef void foo (double *data):
data[some_index_int] = some_value_double
do_something_dependent_on (data)
我像这样分配给data
:
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-D 数组的 dot
),其中:
c = a*b
这里的并行性是在a
行上实现的。检查如何将指针传递给 multiply
函数,以便允许不同的线程共享相同的数组。
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]
要进行检查,您可以使用此脚本:
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))
在我的计算机上,它在哪里提供:
finished dot: 0.601547366526 s
finished mydot: 2.834147917 s
Passed test: True
如果a
的行数小于 col 的数量或b
中的 col 数量,则mydot
会更糟,需要更好地检查要进行并行的维度。
我假设如果没有对data
线程的读取或写入同步锁,线程将读取/写入内存位置并覆盖彼此的更改。如果没有某种同步,您将无法获得一致的结果。
尽管文档 (http://docs.cython.org/src/userguide/parallelism.html) 似乎建议 OpenMP(默认后端)自动创建线程局部变量。