Python的Parfor
我正在寻找MATLAB的parfor for Python(Scipy,Numpy)的最终答案 有没有类似于parfor的解决方案?如果没有,创建一个有什么复杂之处 更新:这是一个典型的数值计算代码,我需要加速Python的Parfor,python,matlab,parallel-processing,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] = HeavyComputationThatIsThre
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]
我一直使用它,但它不是一个完全的模拟,因为我相信它通常使用单独的进程,在某些操作系统上可能会很昂贵。尽管如此,如果循环体足够粗,那么这并不重要,实际上可以带来一些好处。有很多。我碰巧最喜欢的一个是,但我对其他任何一个都不太了解。在IPython中,parfor的一个类似物是
client.MultiEngineClient.map()
或中的一些其他构造。python的内置构造是多处理
文档。我总是使用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
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')
这对我有用!我只是想在这里与大家分享一个特别的例子。我在这里尝试了所有的解决方案,但发现最简单的方法和最接近matlabs parfor的方法是prange 基本上,您可以将循环中的单个字母range更改为prange:
from numba import autojit, prange
@autojit
def parallel_sum(A):
sum = 0.0
for i in prange(A.shape[0]):
sum += A[i]
return sum
这可以通过一个允许您轻松并行化和分发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.
与模块相比,使用Ray有许多优点。特别是,相同的代码将在一台机器以及一组机器上运行。有关Ray的更多优点,请参见
注意:需要记住的一点是,每个远程函数都在单独的进程中执行,可能在不同的机器上执行,因此远程函数的计算应该比调用远程函数花费更多的时间。根据经验,远程函数的计算至少需要10毫秒来分摊远程函数的调度和启动开销。我建议尝试joblib并行 一班轮
指导性 而不是采用for循环
from time import sleep
for _ in range(10):
sleep(.2)
将操作重写为列表
[sleep(.2) for _ in range(10)]
现在,让我们不要直接计算表达式,而是收集应该执行的操作。
这就是延迟方法的作用
from joblib import delayed
[delayed(sleep(.2)) for _ in range(10)]
接下来实例化一个具有n_工作者的并行进程并处理该列表
from joblib import Parallel
r = Parallel(n_jobs=2, verbose=10)(delayed(sleep)(.2) for _ in range(10))
好的,我也试一下,看看我的方法是否更简单 来自多处理导入池的
def重型功能(键):
#对每个键进行一些繁重的计算
输出=键**2
返回键,输出
输出_data={}#+1不知道client.MultiEngineClient,即使我使用了IPython。谢谢你的指导!我不清楚是否可以在脚本模式下运行IPython并行计算框架加速的代码,即不通过IPython运行。@Dat Chu:当然可以。只需将提示时键入的命令写入文件,然后用Python运行即可。(这就是你想要的吗?)到的最新链接。@River是的,优化代码是乏味的,并行性是困难的。我建议您从多处理开始,它是标准库的一部分。您应该将output[ind]
替换为output.flat[ind]
,以使代码正常工作。(output
是一个二维数组,需要两个索引。)@Sven:谢谢。。。这来自于一直在matlab和python之间切换。只有当计算完全由numba支持时,这才会加快速度,单独的过程参见文档也是matlab的parfor
的默认行为。解释如何获取线程,但警告功能有限。提到基于进程的并行性是默认的。
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