在 Python 中并行化 for 循环

计算科学 Python 并行计算
2021-12-01 19:42:44

Python中有没有类似Matlab的parfor的工具?我找到了这个帖子,但它已经有四年历史了。我想也许这里有人可能有更近期的经验。

这是我想要并行化的事物类型的示例:

X = np.random.normal(size=(10, 3))
F = np.zeros((10, ))
for i in range(10):
    F[i] = my_function(X[i,:])

wheremy_function接受ndarray大小(1,3)并返回一个标量。

至少,我想同时使用多个核心——比如 parfor。换句话说,假设一个具有 8 到 16 个内核的共享内存系统。

4个回答

Joblib做你想做的事。基本的使用模式是:

from joblib import Parallel, delayed

def myfun(arg):
     do_stuff
     return result

results = Parallel(n_jobs=-1, verbose=verbosity_level, backend="threading")(
             map(delayed(myfun), arg_instances))

其中是并行计算arg_instances的值列表。myfun主要限制是myfun必须是顶级函数。backend参数可以"threading""multiprocessing"

您可以将其他常用参数传递给并行化函数。的主体myfun也可以引用初始化的全局变量,这些值将可供子级使用。

参数和结果几乎可以是线程后端的任何内容,但结果需要通过多处理后端可序列化。


Dask也提供类似的功能。如果您正在处理核心数据之外的数据,或者您正在尝试并行化更复杂的计算,这可能会更可取。

您正在寻找的是Numba,它可以自动并行化 for 循环。从他们的文档

from numba import jit, prange

@jit
def parallel_sum(A):
    sum = 0.0
    for i in prange(A.shape[0]):
        sum += A[i]

    return sum

不假设my_function选择multiprocessing.Pool().map()有什么特别的东西是并行化这种简单循环的一个很好的猜测。joblib, dask,mpi计算或numba其他答案中提出的类似方法看起来并没有为此类用例带来任何优势,并添加了无用的依赖项(总而言之,它们是矫枉过正的)。使用另一个答案中提出的线程不太可能是一个好的解决方案,因为您必须熟悉代码的 GIL 交互,或者您的代码应该主要执行输入/输出。

也就是说numba,加速顺序纯 python 代码可能是一个好主意,但我觉得这超出了问题的范围。

import multiprocessing
import numpy as np

if __name__ == "__main__":
   #the previous line is necessary under windows to not execute 
   # main module on each child under windows

   X = np.random.normal(size=(10, 3))
   F = np.zeros((10, ))

   pool = multiprocessing.Pool(processes=16)
   # if number of processes is not specified, it uses the number of core
   F[:] = pool.map(my_function, (X[i,:] for i in range(10)) )

但是有一些警告(但不应该影响大多数应用程序):

  • 在windows下没有fork支持,所以在每个孩子启动时都会启动一个带有主模块的解释器,所以它可能会有开销(广告这是导致if __name__ == "__main__"
  • my_function 的参数和结果是腌制和未腌制的,这可能是一个太大的开销,请参阅此答案以减少它https://stackoverflow.com/a/37072511/128629它还使不可拾取的对象无法使用
  • my_function不应依赖于共享状态,例如与全局变量通信,因为状态在进程之间不共享。纯函数(数学意义上的函数)是不共享状态的函数的示例

我对 parfor 的印象是 MATLAB 封装了实现细节,因此它可以同时使用共享内存并行(这是您想要的)和分布式内存并行(如果您正在运行MATLAB 分布式计算服务器)。

如果您想要共享内存并行性,并且您正在执行某种任务并行循环,那么多处理标准库包可能就是您想要的,也许有一个不错的前端,比如doug的帖子中提到的joblib 。标准库不会消失,而且会得到维护,因此风险很低。

还有其他选择,例如Parallel PythonIPython 的并行功能快速浏览一下 Parallel Python 让我觉得它更接近 parfor 的精神,因为该库封装了分布式案例的详细信息,但这样做的代价是您必须采用他们的生态系统。使用 IPython 的成本类似;你必须采用 IPython 的做事方式,这对你来说可能值得,也可能不值得。

如果您关心分布式内存,我推荐mpi4pyLisandro Dalcin 做得很好,并且 mpi4py 用于 PETSc Python 包装器,所以我认为它不会很快消失。与多处理一样,它是一个比 parfor 低(更)级的并行接口,但它可能会持续一段时间。