Python - 循环并行与joblib

问题描述:

我想帮助理解我做了什么/为什么我的代码没有运行,因为我期望。Python - 循环并行与joblib

我已经开始使用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,事情变得非常缓慢。在没有“并行”的情况下运行此代码时,情况会比较慢,直到我将作业数量从1增加为0.

为什么会出现这种情况?我明白我工作越多,脚本越快?我使用这个错误?

谢谢!

+0

首先,您在计算机上运行了多少个CPU或核心? 其次,'n_jobs'设置了同时运行的作业的最大数量。你尝试过'n_jobs = -1'吗?这应该使用您的计算机中的所有CPU。第三,这个for循环的“indeces”有多大? – fedepad

+0

我有24个内核和大量的内存。 indeces约有10,000个条目,所以认为这将是一件好事情并行。我可以尝试n_jobs = -1并回报。 – JB1

+0

是的。我可以想象,如果你将n_jobs从1增加到max(n_jobs = 23,njobs = -1),那么你会达到一个点,在这个点上增加这个数字将涉及更多的开销,所以你必须找到一个最佳点。当然,如果你可以使用后端=“线程”可能会更好,但你必须尝试。 – fedepad

也许你的问题是由于image_pad是一个大阵列而引起的。在您的代码中,您正在使用joblib的默认multiprocessing后端。这个后端创建了一个工作者池,每个工作者都是一个Python进程。然后将该函数的输入数据复制n_jobs次并广播给池中的每个工作人员,这可能导致严重的开销。从joblib的文档引用:

默认情况下池的工人是真正的Python程序使用时n_jobs = 1作为输入传递到并行调用的参数是Python标准库的多模块叉式!序列化并在每个工作进程的内存中重新分配。

这对于大型参数可能会有问题,因为它们将由工人重新分配n_jobs次。

由于在基于numpy的数据结构的科学计算中经常会发生这个问题,joblib.Parallel提供了一个特殊的处理方法,用于大型数组自动将它们转储到文件系统上并将引用传递给worker以打开它们作为内存映射文件使用numpy.ndarray的numpy.memmap子类。这样可以在所有工作进程之间共享一段数据。

注意:以下仅适用于默认的“多处理”后端。如果你的代码可以释放GIL,那么使用后端=“线程化”更加高效。

因此,如果您遇到这种情况,你应该切换到后台线程,如果你能打电话frame时释放全局解释锁,或者切换到joblib共享内存的方式。

docsjoblib提供了一个自动的memmap转换,可能是有用的。

这是很有可能的,你遇到的问题是python编译器的本质的根本。

如果您阅读“https://www.ibm.com/developerworks/community/blogs/jfp/entry/Python_Is_Not_C?lang=en”,可以从专业人士那里了解到,专业人员专门优化和并行化python代码,通过大循环进行迭代对于python线程来说是一项固有的缓慢操作。因此,产生循环数组的更多进程只会减慢速度。

但是 - 有些事情是可以做到的。

CythonNumba编译器都旨在优化代码类似于C/C++风格(即你的情况下) - 尤其是Numba新@vectorise装饰允许标量函数采取并与大数组大型阵列应用操作以平行方式(target=Parallel)。

我不明白你的代码足以给出一个实现的例子,但试试这个!这些编译器以正确的方式使用,在过去并行进程中为我带来了3000,000%的速度提升!