Cython中的临界区

5

在使用Cython中的prange时,如何处理关键部分?

#pragma omp critical 能否使用?

for e in prange(num,nogil=True,num_threads=4):
.
.
.
  #pragma omp critical
  cs
.
.
.
end_for

你试过了吗?有出现任何错误吗? - ForceBru
#pragma是C编译器的预处理指令。我不认为这会起作用。但是我找到了这个链接:http://www.perrygeo.com/parallelizing-numpy-array-loops-with-cython-and-mpi.html - cel
@DavidW能否让gil处理多语句的计算机科学问题? - Yank Leo
1
@DavidW:这就是我认为GIL的工作原理,即每个with-gil-block都将被完全执行,然后才能启动另一个线程中的另一个with-gil-block。但事实并非如此。请参阅文档https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock。我也进行了测试。Python可能会在每行代码之后基本上在另一个线程中执行代码。据我所知,目前无法完全防止此线程切换。Okason提供的openmp解决方案是我找到的第一个干净、简单且有效的解决方案。 - oli
1
@DavidW:啊,那个“任意的Python代码”解释了我在测试中看到的行为。我想知道为什么切换似乎并不像https://docs.python.org/3/library/sys.html#sys.setswitchinterval建议的运行时那样一致。“不那么重要”……好吧,至少我永远不会信任自己,以免犯错并需要几周后才能进行调试。 - oli
显示剩余2条评论
2个回答

3

要处理关键部分,您可以使用OpenMP锁。为此,请使用以下导入:

cimport openmp

然后进行初始化

cdef openmp.omp_lock_t lock
openmp.omp_init_lock(&lock)

并且这样使用它。
with nogil, cython.boundscheck(False), cython.wraparound(False):
    for i in parallel.prange(n, schedule = 'static', 
                             num_threads = num_threads):
        .
        .
        .
        openmp.omp_set_lock(&lock)
        cs
        openmp.omp_unset_lock(&lock)
        .
        .
        .

0

我找到了另一个解决方案,使用Cython和C++11,而不是像Okason的回答中那样使用openmp。

cdef extern from "<mutex>" namespace "std" nogil:
    cdef cppclass recursive_mutex:
        pass
    cdef cppclass lock_guard[T]:
        lock_guard(recursive_mutex mm)

cdef recursive_mutex mtx

cdef void someFun() nogil:
    # lock_guard locks the mutex when it is created
    cdef lock_guard[recursive_mutex]* lck = new lock_guard[recursive_mutex](mtx)
    # do something thread-safely
    del lck
    # lock_guard unlocks the mutex when it is deleted

for ii in prange(4, nogil=True, num_threads=2):
    someFun(ii)

如果我们可以在Cython中将C++对象分配到堆栈中,我们就可以省略del lck,并且在我看来会更好一些;不幸的是,据我所知,Cython不允许这样做。使用上述方法存在忘记解锁的风险,我认为可以直接使用互斥锁(使用mtx.lock())而不是lock_guard
我使用递归互斥锁,因为我的用例通常需要嵌套/递归锁。只是作为一个注释。
我刚刚想出了这个解决方案,我不能说openmp或C++11是推荐的方式。
编辑:可以使用共享指针将其缩减到一行,并实现“更安全”的解锁(在上面的示例中添加/替换这些行)。
from libcpp.memory cimport shared_ptr, make_shared
[...]
cdef shared_ptr[lock_guard[recursive_mutex]] lck = make_shared[lock_guard[recursive_mutex]](mtx)

虽然看起来不太好看。


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