Cython并行prange-线程局部性?

4

我正在使用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在多个线程之间被覆盖了?我做错了什么吗?谢谢!


你说得对。它似乎没有将tEl设为线程本地变量(查看生成的C文件,并搜索“lastprivate”以进行检查)。如果您将其更改为基本类型(如“double”),则可以正常工作,但是似乎无法使用Cython对象类型。我不知道明显的解决方案,但在GitHub上提交错误可能值得一试。 - DavidW
2个回答

5

看起来Cython故意选择将任何Python变量(包括Cython的cdef class)从线程本地变量列表中排除。

代码

我怀疑这是出于避免引用计数问题而故意为之,因为他们需要在循环结束时减少所有线程本地变量的引用计数(这不是无法克服的问题,但可能是一个重大的改变)。因此,我认为它不太可能被修复,但更新文档可能会有所帮助。

解决方法是将循环体重构为一个函数,在该函数中每个变量都最终变成“局部”变量,因此这不是一个问题:

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()

好的,我明白了。不幸的是,并行调用cdef扩展似乎是不可能的。目前重构为纯cdef函数调用并不现实。在你的例子中,f()没有使用nogil标志声明,因此不能并行执行? - mneuner
如果调用refcount tEl,则需要在开头和结尾处使用gil。它可以在内部释放gil,这部分将并行运行。在这个玩具示例中,它不应该比您问题中的代码更糟糕(显然几乎没有“无gil”工作)。 - DavidW

0

谢谢您的快速回复。然而,编译时出现以下错误:
cdef 语句不允许在此处 <<< (这个问题在发布的链接中也提到了吗?)
- mneuner
好的,我打开了很多标签页,找不到我看到cdef移动的地方了。重新阅读你的示例,我没有看到一个函数。通常,Cython文件会被编译成模块,并调用生成的函数。上面的代码是完整的文件吗? - Pierre de Buyl
其实链接里已经有了。搜索 "cdef w = Worker(n) # 块作用域的 cdef" - Pierre de Buyl
tEl是cython扩展(cdef类)的一个实例。其中一个nogil方法被调用,但这与发布的问题无关。基本上,这个片段会产生错误。 - mneuner
首次测试表明,即使在“with gil”块之间,tid也无法保留。 - Pierre de Buyl
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接