代码之家  ›  专栏  ›  技术社区  ›  JB1

Python-使用joblib进行循环并行

  •  12
  • JB1  · 技术社区  · 8 年前

    我希望有人能帮助我理解我到底做了什么/为什么我的代码没有像我预期的那样运行。

    我已经开始使用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增加到1。

    为什么会这样?我知道我运行的作业越多,脚本就越快?我用错了吗?

    谢谢

    2 回复  |  直到 8 年前
        1
  •  3
  •   Community Johnny    4 年前

    也许你的问题是因为 image_pad 是一个大数组。在代码中,您使用的是默认值 multiprocessing 的后端 joblib 。此后端创建一个工作线程池,每个工作线程都是一个Python进程。然后复制函数的输入数据 n_jobs 时间和广播给池中的每个工人,这可能会导致严重的开销。引用自 joblib 的文档:

    默认情况下,池的工作线程是真正的Python进程,当n_jobs!=1.作为Parallel调用的输入传递的参数被序列化并重新分配到每个工作进程的内存中。

    这对于大型参数来说可能有问题,因为工人会重新分配n_jobs时间。

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

    注意:以下仅适用于默认的“多处理”后端。如果您的代码可以发布GIL,那么使用backend=“threading”会更有效。

    因此,如果这是您的情况,那么您应该切换到线程后端,如果您能够在调用时释放全局解释器锁 frame ,或切换到的共享内存方法 作业库 .

    这个 docs 这么说吧 作业库 提供自动化 memmap 可能有用的转换。

        2
  •  3
  •   Isky Mathews    7 年前

    您遇到的问题很可能是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 ).

    我不太理解您的代码,无法给出一个实现示例,但请尝试一下!这些编译器以正确的方式使用,在过去为我带来了3000000%的并行进程速度提升!