我正在寻找对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]
答案 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)
要查看一个示例,请考虑在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)
答案 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是一个字典,它将为每个键包含该键的计算结果。
就是这样。