我想帮助理解我所做的事情/为什么我的代码没有像我期望的那样运行。
我已经开始使用joblib通过并行运行(大)循环来尝试加速我的代码。
我正在使用它:
from joblib import Parallel, delayed
def frame(indeces, image_pad, m):
XY_Patches = np.float32(image_pad[indeces[0]:indeces[0]+m, indeces[1]:indeces[1]+m, indeces[2]])
XZ_Patches = np.float32(image_pad[indeces[0]:indeces[0]+m, indeces[1], indeces[2]:indeces[2]+m])
YZ_Patches = np.float32(image_pad[indeces[0], indeces[1]:indeces[1]+m, indeces[2]:indeces[2]+m])
return XY_Patches, XZ_Patches, YZ_Patches
def Patch_triplanar_para(image_path, patch_size):
Image, Label, indeces = Sampling(image_path)
n = (patch_size -1)/2
m = patch_size
image_pad = np.pad(Image, pad_width=n, mode='constant', constant_values = 0)
A = Parallel(n_jobs= 1)(delayed(frame)(i, image_pad, m) for i in indeces)
A = np.array(A)
Label = np.float32(Label.reshape(len(Label), 1))
R, T, Y = np.hsplit(A, 3)
return R, T, Y, Label
我一直在试验“n_jobs”,期望增加这个会加速我的功能。然而,随着我增加n_jobs,事情变得非常显着。在没有“Parallel”的情况下运行此代码时,事情会变慢,直到我从1增加作业数量。
为什么会这样?我明白我运行的工作越多,脚本越快?我用错了吗?
谢谢!
答案 0 :(得分:3)
也许您的问题是由于image_pad
是一个大数组引起的。在您的代码中,您使用的是multiprocessing
的默认joblib
后端。这个后端创建了一个工作池,每个工作池都是一个Python进程。然后将函数的输入数据复制n_jobs
次并广播给池中的每个工作者,这可能导致严重的开销。引自joblib的文档:
默认情况下,当n_jobs!= 1时,池的工作者是使用Python标准库的多处理模块分叉的真实Python进程。作为输入传递给Parallel调用的参数被序列化并在每个工作进程的内存中重新分配
这对于大型论证来说可能会有问题,因为工作人员将重新分配n_jobs次。
由于这个问题经常发生在基于numpy的数据结构的科学计算中,joblib.Parallel为大型数组提供了一种特殊的处理方式,可以自动将它们转储到文件系统上,并将引用传递给worker,将它们打开为该文件的内存映射使用numpy.ndarray的numpy.memmap子类。这样就可以在所有工作进程之间共享一段数据。
注意:以下内容仅适用于默认的"多处理"后端。如果您的代码可以释放GIL,那么使用backend =" threading"更有效率。
因此,如果是这种情况,如果能够在调用frame
时释放全局解释器锁,或者切换到joblib
的共享内存方法,则应切换到线程后端。
docs表示joblib
提供了有用的自动memmap
转化。
答案 1 :(得分:3)
你遇到的问题很可能是python编译器性质的基本问题。
如果您阅读" https://www.ibm.com/developerworks/community/blogs/jfp/entry/Python_Is_Not_C?lang=en",您可以从专业人员那里看到,他们专注于优化和并行化python代码,迭代大循环是python线程执行的一个固有的慢操作。因此,产生更多循环遍历数组的进程只会减慢速度。
然而 - 有些事情可以做。
Cython和Numba编译器都旨在优化类似于C / C ++风格的代码(即您的情况) - 尤其是Numba的新@vectorise装饰器允许标量函数以并行方式(target=Parallel
)接收大型数组并对其进行操作。
我不太了解您的代码足以举例说明实现,但试试这个!这些编译器以正确的方式使用,在过去为并行进程带来了300,000%的速度提升!