Parfor for Python

时间:2011-01-13 16:28:55

标签: python matlab parallel-processing

我正在寻找对MATLAB的parfor for Python(Scipy,Numpy)的明确答案。

是否有类似于parfor的解决方案?如果没有,创建一个的复杂性是什么?

更新:这是我需要加速的典型数值计算代码

import numpy as np

N = 2000
output = np.zeros([N,N])
for i in range(N):
    for j in range(N):
        output[i,j] = HeavyComputationThatIsThreadSafe(i,j)

重计算函数的一个例子是:

import scipy.optimize

def HeavyComputationThatIsThreadSafe(i,j):
    n = i * j

    return scipy.optimize.anneal(lambda x: np.sum((x-np.arange(n)**2)), np.random.random((n,1)))[0][0,0]

8 个答案:

答案 0 :(得分:27)

内置于python的那个是multiprocessing文档是here。我总是使用multiprocessing.Pool和处理器一样多的工人。然后,每当我需要执行类似for循环的结构时,我使用Pool.imap

只要你的函数体不依赖于任何先前的迭代,那么你应该接近线性加速。这也要求您的输入和输出为pickle - 但是这对于标准类型来说非常容易确保。

更新: 更新函数的一些代码只是为了表明它是多么容易:

from multiprocessing import Pool
from itertools import product

output = np.zeros((N,N))
pool = Pool() #defaults to number of available CPU's
chunksize = 20 #this may take some guessing ... take a look at the docs to decide
for ind, res in enumerate(pool.imap(Fun, product(xrange(N), xrange(N))), chunksize):
    output.flat[ind] = res

答案 1 :(得分:19)

有很多Python frameworks for parallel computing。我最喜欢的那个是IPython,但我对其他任何一个都不太了解。在IPython中,parfor的一个类似物是client.MultiEngineClient.map()the documentation on quick and easy parallelism中的其他一些结构。

答案 2 :(得分:4)

我一直使用Parallel Python但它不是一个完整的模拟,因为我认为它通常使用单独的过程,这在某些操作系统上可能很昂贵。但是,如果你的循环体足够厚实,那么这无关紧要,实际上可以带来一些好处。

答案 3 :(得分:4)

Jupyter笔记本

要查看一个示例,请考虑在Python中编写此Matlab代码的等效性

matlabpool open 4
parfor n=0:9
   for i=1:10000
       for j=1:10000
           s=j*i   
       end
   end
   n
end
disp('done')

人们可以在python中写这个,特别是在jupyter笔记本中。你必须在工作目录中创建一个函数(我称之为FunForParFor.py),该函数具有以下内容

def func(n):
    for i in range(10000):
        for j in range(10000):
            s=j*i
    print(n)

然后我转到我的Jupyter笔记本并编写以下代码

import multiprocessing  
import FunForParFor

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)
    pool.map(FunForParFor.func, range(10))
    pool.close()
    pool.join()   
    print('done')

这对我有用!我只是想在这里分享给你一个特定的例子。

答案 4 :(得分:2)

这可以通过Ray优雅地完成,该系统使您可以轻松地并行化和分发Python代码。

要并行化示例,您需要使用@ray.remote装饰器定义函数,然后使用.remote调用它们。

import numpy as np
import time

import ray

ray.init()

# Define the function. Each remote function will be executed 
# in a separate process.
@ray.remote
def HeavyComputationThatIsThreadSafe(i, j):
    n = i*j
    time.sleep(0.5) # Simulate some heavy computation. 
    return n

N = 10
output_ids = []
for i in range(N):
    for j in range(N):
        # Remote functions return a future, i.e, an identifier to the 
        # result, rather than the result itself. This allows invoking
        # the next remote function before the previous finished, which
        # leads to the remote functions being executed in parallel.
        output_ids.append(HeavyComputationThatIsThreadSafe.remote(i,j))

# Get results when ready.
output_list = ray.get(output_ids)
# Move results into an NxN numpy array.
outputs = np.array(output_list).reshape(N, N)

# This program should take approximately N*N*0.5s/p, where
# p is the number of cores on your machine, N*N
# is the number of times we invoke the remote function,
# and 0.5s is the time it takes to execute one instance
# of the remote function. For example, for two cores this
# program will take approximately 25sec. 

multiprocessing模块相比,使用Ray有许多优点。特别是,相同的代码将在单台计算机以及多台计算机上运行。有关Ray的更多优点,请参见this related post

注意:需要牢记的一点是,每个远程功能都是在单独的进程中执行的,可能是在不同的机器上执行的,因此,远程功能的计算比调用远程功能要花更多的时间。 。根据经验,远程功能的计算至少要花费几十毫秒,才能分摊远程功能的调度和启动开销。

答案 5 :(得分:1)

我在这里尝试了所有解决方案,但发现最简单的方法和最接近于matlabs parfor的等效方法是numba's prange。

基本上,您可以在循环中更改单个字母,范围为prange:

pt.0

答案 6 :(得分:1)

我建议并行尝试joblib。

一个班轮

from joblib import Parallel, delayed
out = Parallel(n_jobs=2)(delayed(heavymethod)(i) for i in range(10)) 

说明性

而不是进行for循环

from time import sleep
for _ in range(10):
   sleep(.2)

将您的操作重写为列表理解

[sleep(.2) for _ in range(10)]

现在让我们不要直接评估表达式,而是收集应该执行的操作。 这就是delayed方法的目的。

from joblib import delayed
[delayed(sleep(.2)) for _ in range(10)]

下一步,使用n_workers实例化并行过程并处理列表。

from joblib import Parallel
r = Parallel(n_jobs=2, verbose=10)(delayed(sleep)(.2) for _ in range(10)) 
[Parallel(n_jobs=2)]: Done   1 tasks      | elapsed:    0.6s
[Parallel(n_jobs=2)]: Done   4 tasks      | elapsed:    0.8s
[Parallel(n_jobs=2)]: Done  10 out of  10 | elapsed:    1.4s finished

答案 7 :(得分:0)

好吧,我也会尝试一下,看看我的方式是否更简单

from multiprocessing import Pool
def heavy_func(key):
    #do some heavy computation on each key 
    output = key**2
    return key, output 

output_data ={}     #<--this dict will store the results
keys = [1,5,7,8,10] #<--compute heavy_func over all the values of keys
with Pool(processes=40) as pool:
    for i in pool.imap_unordered(heavy_func, keys):
        output_data[i[0]] = i[1]

现在output_data是一个字典,它将为每个键包含该键的计算结果。

就是这样。