web-dev-qa-db-fra.com

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? Si non, 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)

Voici un exemple de fonction de calcul lourde:

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]
43
Dat Chu

Il existe de nombreux frameworks Python pour l'informatique parallèle . Celui que j'aime le plus est IPython , mais je ne connais pas trop les autres. Dans IPython, un analogue à parfor serait client.MultiEngineClient.map() ou quelques-uns des autres constructs de la documentation sur le parallélisme rapide et facile .

19
Sven Marnach

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 boucle semblable à une structure, 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 vitesse quasi linéaire. Cela nécessite également que vos entrées et sorties soient pickle- mais ceci est assez facile à garantir pour les types standard.

UPDATE: Quelques codes pour votre fonction mise à jour, juste pour montrer à quel point il 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
27
JudoWill

J'ai toujours utilisé Parallel Python mais ce n'est pas un analogue complet, car je crois qu'il utilise généralement des processus distincts, ce qui peut coûter cher sur certains systèmes d'exploitation. Néanmoins, si le corps de vos boucles est suffisamment épais, cela n’aura aucune incidence et peut présenter certains avantages.

4
David Heffernan

Jupyter Notebook

Pour voir un exemple, considérez que vous souhaitez é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 cela en python, en particulier dans le cahier jupyter. Vous devez créer une fonction dans le répertoire de travail (je l’ai appelée FunForParFor.py) qui a les caractéristiques suivantes

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

Ensuite, je vais sur mon carnet Jupyter et écris le code suivant

import multiprocessing  
import FunForParFor

if __== '__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. 

3
rsc05

Cela peut être fait élégamment avec Ray , un système qui vous permet de paralléliser et de distribuer facilement votre code Python.

Pour paralléliser votre exemple, vous devez définir vos fonctions avec le décorateur @ray.remote, puis les appeler avec .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. 

L'utilisation de Ray sur le module multitraitement présente de nombreux avantages. En particulier, le code same s'exécutera sur une seule machine ainsi que sur un cluster de machines. Pour plus d’avantages de Ray, voir cet article connexe .

Remarque: Il convient de garder à l'esprit que chaque fonction distante est exécutée dans un processus séparé, éventuellement sur une machine différente, et que le calcul de la fonction distante devrait donc prendre plus que l'invocation d'une fonction distante. En règle générale, le calcul d'une fonction distante devrait prendre au moins quelques 10 secondes pour amortir l'ordonnancement et la surcharge de démarrage d'une fonction distante.

2
Ion Stoica

J’ai essayé toutes les solutions ici, mais j’ai trouvé que la méthode la plus simple et la plus proche de matlabs parfor est la syntaxe de numba .

Essentiellement, vous modifiez une seule lettre dans votre boucle, une plage à pranger:

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
1
Felix