Parfor pour Python
Je cherche une réponse définitive au parfor de MATLAB pour Python (Scipy, Numpy).
Existe-t-il une solution similaire à parfor? Sinon, quelle est la complication pour en créer un?
UPDATE: voici un code de calcul numérique typique dont j'ai besoin pour accélérer
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)
Un exemple de fonction de calcul lourde est:
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]
5 réponses
Il existe de nombreux frameworks Python pour le calcul parallèle . Celui que j'aime le plus est - IPython, mais je ne sais pas trop sur les autres. Dans IPython, un analogue de parfor serait client.MultiEngineClient.map()
ou certaines des autres constructions dans la documentation sur le parallélisme rapide et facile .
Celui intégré à python serait multiprocessing
docs sont ici. J'utilise toujours multiprocessing.Pool
avec autant de travailleurs que de processeurs. Ensuite, chaque fois que j'ai besoin de faire une structure de type for-loop, j'utilise Pool.imap
Tant que le corps de votre fonction ne dépend d'aucune itération précédente, vous devriez avoir une accélération presque linéaire. Cela nécessite également que vos entrées et sorties soient pickle
- capables, mais c'est assez facile à assurer pour les types standard.
Mise à jour: Certains code pour votre mise à jour fonction juste pour montrer à quel point c'est facile:
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
J'ai toujours utilisé Python parallèle mais ce n'est pas un analogue complet car je crois qu'il utilise généralement des processus séparés qui peuvent être coûteux sur certains systèmes d'exploitation. Pourtant, si le corps de vos boucles est assez volumineux, cela n'a pas d'importance et peut effectivement avoir des avantages.
Jupyter Notebook
Pour voir un exemple, considérez que vous voulez écrire l'équivalence de ce code Matlab en Python
matlabpool open 4
parfor n=0:9
for i=1:10000
for j=1:10000
s=j*i
end
end
n
end
disp('done')
La façon dont on peut écrire ceci en python en particulier dans Jupyter notebook. Vous devez créer une fonction dans le répertoire de travail (je l'ai appelé FunForParFor.py) qui a le
def func(n):
for i in range(10000):
for j in range(10000):
s=j*i
print(n)
Ensuite, je vais à mon carnet Jupyter et écris le code suivant
import multiprocessing
import FunForParFor
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4)
pool.map(FunForParFor.func, range(10))
pool.close()
pool.join()
print('done')
Cela a fonctionné pour moi! Je voulais juste le partager ici, pour vous donner un exemple particulier.
J'ai essayé toutes les solutions ici, mais j'ai trouvé que le moyen le plus simple et le plus proche équivalent à matlabs parfor est Prange de numba.
Essentiellement, vous changez une seule lettre dans votre boucle, 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