Python的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

我正在寻找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]

我一直使用它,但它不是一个完全的模拟,因为我相信它通常使用单独的进程,在某些操作系统上可能会很昂贵。尽管如此,如果循环体足够粗,那么这并不重要,实际上可以带来一些好处。

有很多。我碰巧最喜欢的一个是,但我对其他任何一个都不太了解。在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