将 openMP 与 Cython 结合使用:并行化内部循环



(如您所见,我对Python GIL和PYTHON(或cython(中的多线程等概念不是很熟悉(

我在 Cython 中编写了一个函数,它由一段带有双 for 循环的代码片段组成,其中重复调用函数 f。

for i in range(I):
for j in range(J):
res=f(A[i],B[j])

我有一台有 4 个 CPU 内核的机器,我想并行化不是第一个而是第二个循环。 我发现了这个很棒的网站,但它没有处理内部循环的情况,也没有详细介绍。 所以在我看来,我可以写:

for i in range(I):
#In what case can I release the GIL safely ? Is that necessary at all ?
with nogil, parallel(num_threads=4):
for j in prange(J,shedule="dynamic"):
res=f(A[i],B[j])

那行得通吗?我是否必须将 with nogil 放在两个循环之外,这样它就不会反复运行到它释放和"捕获"这个 GIL 的东西?有人可以向我解释一下写这些陈述背后的逻辑和逻辑是什么,以便我能够概括到看不见的问题。

释放和重新捕获 GIL 需要时间成本,设置并行循环也需要时间成本。出于这个原因,通常最好将最外层的循环设置为并行循环。但是,如果您有充分的理由专门要并行化内部循环,那么它将起作用,并且希望与f中包含的实际工作相比,成本应该很小。

释放 GIL 可防止您访问 Python 变量和调用 Python 函数。类型化的Cython变量,cdef函数和Cython内存视图工作正常。您将通过尽可能远离with nogil:来获得一个小的加速。因此,如果可能的话,把它放在外循环周围,但如果不可能,那么在你展示它的地方

就可以了。有必要释放到 GIL 以进行prange循环。如有必要,您可以在循环中回收它(with gil(,但尝试只对循环的一小部分执行此操作,并且仅在需要时执行此操作(需要 GIL 的代码不能与其他需要 GIL 的代码并行运行(。

对于并行代码,行res=f(A[i],B[j])有点奇怪,因为只会保存最后一个循环中的res。通常你会写入数组的元素(例如res[i,j]=f(A[i],B[j])(。但是,像您展示的那样这样做可能有充分的理由......

Cython 会(通常(警告您,如果您尝试做需要 GIL 的事情,所以一个好主意是尝试一下看看。

最新更新