12

我需要一些帮助来准确了解我所做的事情/为什么我的代码没有按我的预期运行。

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

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

谢谢!

4

2 回答 2

3

也许你的问题是因为image_pad是一个大数组而引起的。在您的代码中,您使用的multiprocessingjoblib. 这个后端创建了一个工人池,每个工人都是一个 Python 进程。然后将函数的输入数据复制n_jobs多次并广播到池中的每个工作人员,这可能会导致严重的开销。引用joblib的文档:

默认情况下,池中的工作人员是真正的 Python 进程,当 n_jobs != 1 时,使用 Python 标准库的多处理模块派生。作为输入传递给 Parallel 调用的参数被序列化并在每个工作进程的内存中重新分配。

这对于大参数来说可能是有问题的,因为它们将被工作人员重新分配 n_jobs 次。

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

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

所以如果是这种情况,你应该切换到线程后端,如果你能够在调用时释放全局解释器锁frame,或者切换到joblib.

文档说这提供了一种可能有用joblib的自动转换。memmap

于 2017-01-08T00:54:43.400 回答
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)。

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

于 2017-01-08T17:44:27.977 回答