Pour C++, nous pouvons utiliser OpenMP pour faire de la programmation parallèle. Cependant, OpenMP ne fonctionnera pas pour Python. Que dois-je faire si je veux mettre en parallèle certaines parties de mon programme python?
La structure du code peut être considérée comme:
solve1(A)
solve2(B)
Où solve1
et solve2
sont deux fonctions indépendantes. Comment exécuter ce type de code en parallèle plutôt qu'en séquence afin de réduire le temps d'exécution? Merci beaucoup d'avance ... Le code est:
def solve(Q, G, n):
i = 0
tol = 10 ** -4
while i < 1000:
inneropt, partition, x = setinner(Q, G, n)
outeropt = setouter(Q, G, n)
if (outeropt - inneropt) / (1 + abs(outeropt) + abs(inneropt)) < tol:
break
node1 = partition[0]
node2 = partition[1]
G = updateGraph(G, node1, node2)
if i == 999:
print "Maximum iteration reaches"
print inneropt
Où setinner et setouter sont deux fonctions indépendantes. C'est là que je veux mettre en parallèle ...
Vous pouvez utiliser le module multitraitement . Dans ce cas, je pourrais utiliser un pool de traitement:
from multiprocessing import Pool
pool = Pool()
result1 = pool.apply_async(solve1, [A]) # evaluate "solve1(A)" asynchronously
result2 = pool.apply_async(solve2, [B]) # evaluate "solve2(B)" asynchronously
answer1 = result1.get(timeout=10)
answer2 = result2.get(timeout=10)
Cela engendrera des processus pouvant effectuer un travail générique à votre place. Puisque nous n'avons pas passé processes
, il créera un processus pour chaque cœur de processeur de votre ordinateur. Chaque cœur de processeur peut exécuter un processus simultanément.
Si vous souhaitez mapper une liste à une seule fonction, procédez comme suit:
args = [A, B]
results = pool.map(solve1, args)
N'utilisez pas de threads, car GIL verrouille les opérations sur les objets python.
Cela peut être fait très élégamment avec Ray .
Pour paralléliser votre exemple, vous devez définir vos fonctions avec le décorateur @ray.remote
, puis les appeler avec .remote
.
import ray
ray.init()
# Define the functions.
@ray.remote
def solve1(a):
return 1
@ray.remote
def solve2(b):
return 2
# Start two tasks in the background.
x_id = solve1.remote(0)
y_id = solve2.remote(1)
# Block until the tasks are done and get the results.
x, y = ray.get([x_id, y_id])
Cela présente de nombreux avantages par rapport au module multitraitement .
Ces appels de fonction peuvent être composés ensemble, par exemple,
@ray.remote
def f(x):
return x + 1
x_id = f.remote(1)
y_id = f.remote(x_id)
z_id = f.remote(y_id)
ray.get(z_id) # returns 4
Notez que Ray est un framework que j'ai aidé à développer.
CPython utilise le Global Interpreter Lock, ce qui rend la programmation parallèle un peu plus intéressante que C++.
Ce sujet contient plusieurs exemples et descriptions utiles du défi:
La solution, comme d'autres l'ont dit, consiste à utiliser plusieurs processus. Le cadre le plus approprié dépend toutefois de nombreux facteurs. En plus de ceux déjà mentionnés, il y a aussi charm4py et mpi4py (je suis le développeur de charm4py).
Il existe un moyen plus efficace d'implémenter l'exemple ci-dessus que d'utiliser l'abstraction du pool de travail. La boucle principale envoie les mêmes paramètres (y compris le graphe complet G
) à plusieurs reprises aux travailleurs dans chacune des 1000 itérations. Puisqu'au moins un travailleur réside sur un processus différent, cela implique la copie et l'envoi des arguments à l'autre processus. Cela pourrait être très coûteux en fonction de la taille des objets. Au lieu de cela, il est logique que les travailleurs stockent l'état et envoient simplement les informations mises à jour.
Par exemple, dans charm4py cela peut être fait comme ceci:
class Worker(Chare):
def __init__(self, Q, G, n):
self.G = G
...
def setinner(self, node1, node2):
self.updateGraph(node1, node2)
...
def solve(Q, G, n):
# create 2 workers, each on a different process, passing the initial state
worker_a = Chare(Worker, onPE=0, args=[Q, G, n])
worker_b = Chare(Worker, onPE=1, args=[Q, G, n])
while i < 1000:
result_a = worker_a.setinner(node1, node2, ret=True) # execute setinner on worker A
result_b = worker_b.setouter(node1, node2, ret=True) # execute setouter on worker B
inneropt, partition, x = result_a.get() # wait for result from worker A
outeropt = result_b.get() # wait for result from worker B
...
Notez que pour cet exemple, nous n'avons besoin que d'un seul ouvrier. La boucle principale peut exécuter l'une des fonctions et faire en sorte que l'opérateur exécute l'autre. Mais mon code aide à illustrer un certain nombre de choses:
result_a.get()
est bloqué en attente du résultat, le travailleur A effectue le calcul dans le même processus.