【问题标题】:Cython parallel prange - thread locality?Cython并行prange - 线程局部性?
【发布时间】:2017-02-09 18:44:20
【问题描述】:

我正在使用 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 上提交一个错误

标签: multithreading parallel-processing cython


【解决方案1】:

看起来 Cython 故意选择从线程局部变量列表中排除任何 Python 变量(包括 Cython cdef classes)。 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()

【讨论】:

  • 好的,我明白了。不幸的是,似乎不可能并行调用 cdef 扩展。目前重构为纯 cdef 函数调用是不现实的。在您的示例中, f() 是在没有 nogil 标志的情况下声明的,因此不会并行执行?
  • 如果调用 refcount tEl,它需要在开始和结束时使用 gil。它可以释放内部的 gil,并且该位将并行运行。它应该不会比您问题中的代码差(显然在这个玩具示例中几乎没有“无 gil”的工作)。
【解决方案2】:

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

如果你希望tEl 是线程私有的,你不应该在全局范围内定义它。尝试在 prange 内移动 cdef CythonElement tEl。见http://cython-devel.python.narkive.com/atEB3yrQ/openmp-thread-private-variable-not-recognized-bug-report-discussion(私有变量的一部分)。

【讨论】:

  • 感谢您的快速回复。但是,编译给了我:>>>此处不允许使用cdef语句
  • 好的,我打开了许多选项卡,我找不到看到 cdef 移动的地方。重新阅读您的示例,我没有看到功能。通常,cython 文件被编译为模块并调用生成的函数。上面的代码是完整文件吗?
  • 实际上在链接中。搜索 "cdef w = Worker(n) # 块范围的 cdef"
  • tEl 是 cython 扩展(cdef 类)的一个实例。之后调用它的一种 nogil 方法,但这与发布的问题无关。基本上,这个 sn-p 会产生错误。
  • >>> 实际上在链接中。搜索 "cdef w = Worker(n) # block-scoped cdef
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多