(正如您将看到我对Python GIL和多线程IN PYTHON(或cython)等概念不太熟悉)
我在Cython中编写了一个函数,它由一个带有double for循环的代码片段组成,其中函数f被重复调用。
for i in range(I):
for j in range(J):
res=f(A[i],B[j])
我有一台带有4个CPU内核的机器,我希望不是第一个而是第二个循环并行化。 我找到了this wonderful website,但它没有处理内循环的情况,也没有详细说明。 所以在我看来我可以写:
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])
那会有用吗?我是否必须在两个循环之外放置nogil,以便它不会重复运行到其中并释放"捕获"这个GIL的事情?有人可以向我解释编写这些陈述背后的逻辑是什么,以便我能够概括到无法解决的问题。
答案 0 :(得分:2)
释放和重新捕获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])
)。但是,可能有充分的理由像你所展示的那样......
如果你尝试做一些需要GIL的事情,Cython会(通常)警告你,所以一个好主意是尝试并看看。