Cython并行prange - 线程局部性?

时间:2017-02-09 18:44:20

标签: multithreading parallel-processing cython

我正在使用prange迭代这样的列表:

from cython.parallel import  prange, threadid

cdef int tid
cdef CythonElement tEl
cdef int a, b, c

# elList: python list of CythonElement instances is passed via function call
for n in prange(nElements, schedule='dynamic', nogil=True):
    with gil:
        tEl = elList[n]
        tid =  threadid()
        a = tEl.a
        b = tEl.b
        c = tEl.c 

        print("thread {:} elnumber {:}".format(tid, tEl.elNumber))

   #nothing is done here

    with gil:
        print("thread {:} elnumber {:}".format(tid, tEl.elNumber))

    # some other computations based on a, b and c here ...

我期待这样的输出:

thread 0 elnumber 1
thread 1 elnumber 2
thread 2 elnumber 3
thread 3 elnumber 4
thread 0 elnumber 1
thread 1 elnumber 2
thread 2 elnumber 3
thread 3 elnumber 4

但我明白了:

thread 1 elnumber 1
thread 0 elnumber 3
thread 3 elnumber 2
thread 2 elnumber 4
thread 3 elnumber 4
thread 1 elnumber 2
thread 0 elnumber 4
thread 2 elnumber 4

那么,某种程度上线程局部变量tEl会被跨越线程覆盖?我究竟做错了什么 ?谢谢!

2 个答案:

答案 0 :(得分:3)

看起来Cython故意选择从线程局部变量列表中排除任何Python变量(包括Cython cdef class es)。 Code

我怀疑这是故意避免引用计数问题 - 它们需要在循环结束时删除所有线程局部变量的引用计数(这不是一个不可克服的问题,但可能是一个大变化)。因此,我认为不太可能修复,但文档更新可能会有所帮助。

解决方案是将循环体重构为一个函数,其中每个变量都有效地“本地”到函数,因此它不是问题:

cdef f(CythonElement tEl):
    cdef int tid
    with nogil:
        tid = threadid()
        with gil:
            print("thread {:} elnumber {:}".format(tid, tEl.elNumber))

        with gil:
            print("thread {:} elnumber {:}".format(tid, tEl.elNumber))

   # I've trimmed the function a bit for the sake of being testable

# then for the loop:
for n in prange(nElements, schedule='dynamic', nogil=True):
    with gil:
        f()

答案 1 :(得分:0)

Cython提供基于线程的并行性。不保证执行线程的顺序,因此thread的无序值。

如果您希望tEl对该线程是私有的,则不应全局定义它。尝试在prange中移动cdef CythonElement tEl。请参阅http://cython-devel.python.narkive.com/atEB3yrQ/openmp-thread-private-variable-not-recognized-bug-report-discussion(部分关于私有变量)。