C'est probablement une question triviale, mais comment puis-je paralléliser la boucle suivante en python?
# setup output lists
output1 = list()
output2 = list()
output3 = list()
for j in range(0, 10):
# calc individual parameter value
parameter = j * offset
# call the calculation
out1, out2, out3 = calc_stuff(parameter = parameter)
# put results into correct output list
output1.append(out1)
output2.append(out2)
output3.append(out3)
Je sais comment démarrer des threads simples dans Python mais je ne sais pas comment "collecter" les résultats.
Plusieurs processus conviendraient également - tout ce qui est le plus simple pour ce cas. J'utilise actuellement Linux, mais le code devrait également fonctionner sous Windows et Mac.
Quel est le moyen le plus simple de paralléliser ce code?
L'utilisation de plusieurs threads sur CPython ne vous donnera pas de meilleures performances pour le code pur-Python en raison du verrou d'interpréteur global (GIL). Je suggère d'utiliser le module multiprocessing
à la place:
pool = multiprocessing.Pool(4)
out1, out2, out3 = Zip(*pool.map(calc_stuff, range(0, 10 * offset, offset)))
Notez que cela ne fonctionnera pas dans l'interpréteur interactif.
Pour éviter le FUD habituel autour du GIL: L'utilisation de threads pour cet exemple ne présente aucun avantage. Vous voulez utiliser des processus ici, pas des threads, car ils évitent toute une série de problèmes.
Pour paralléliser une simple boucle for, joblib apporte une grande valeur à l’utilisation brute du multitraitement. Non seulement la syntaxe courte, mais aussi des choses telles que le regroupement transparent d'itérations lorsqu'elles sont très rapides (pour supprimer la surcharge) ou la capture de la trace du processus enfant pour obtenir un meilleur rapport d'erreurs.
Disclaimer: Je suis l'auteur original de joblib.
Quel est le moyen le plus simple de paralléliser ce code?
J'aime beaucoup concurrent.futures
pour cela, disponible en Python3 depuis la version 3.2 - et via backport vers 2.6 et 2.7 sur PyPi .
Vous pouvez utiliser des threads ou des processus et utiliser exactement la même interface.
Mettez ceci dans un fichier - futuretest.py:
import concurrent.futures
import time, random # add some random sleep time
offset = 2 # you don't supply these so
def calc_stuff(parameter=None): # these are examples.
sleep_time = random.choice([0, 1, 2, 3, 4, 5])
time.sleep(sleep_time)
return parameter / 2, sleep_time, parameter * parameter
def procedure(j): # just factoring out the
parameter = j * offset # procedure
# call the calculation
return calc_stuff(parameter=parameter)
def main():
output1 = list()
output2 = list()
output3 = list()
start = time.time() # let's see how long this takes
# we can swap out ProcessPoolExecutor for ThreadPoolExecutor
with concurrent.futures.ProcessPoolExecutor() as executor:
for out1, out2, out3 in executor.map(procedure, range(0, 10)):
# put results into correct output list
output1.append(out1)
output2.append(out2)
output3.append(out3)
finish = time.time()
# these kinds of format strings are only available on Python 3.6:
# time to upgrade!
print(f'original inputs: {repr(output1)}')
print(f'total time to execute {sum(output2)} = sum({repr(output2)})')
print(f'time saved by parallelizing: {sum(output2) - (finish-start)}')
print(f'returned in order given: {repr(output3)}')
if __== '__main__':
main()
Et voici la sortie:
$ python3 -m futuretest
original inputs: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
total time to execute 33 = sum([0, 3, 3, 4, 3, 5, 1, 5, 5, 4])
time saved by parallellizing: 27.68999981880188
returned in order given: [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]
Remplacez maintenant ProcessPoolExecutor
par ThreadPoolExecutor
, puis exécutez à nouveau le module:
$ python3 -m futuretest
original inputs: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
total time to execute 19 = sum([0, 2, 3, 5, 2, 0, 0, 3, 3, 1])
time saved by parallellizing: 13.992000102996826
returned in order given: [0, 4, 16, 36, 64, 100, 144, 196, 256, 324]
Maintenant, vous avez fait du multithreading et du multitraitement!
L'échantillonnage est beaucoup trop petit pour comparer les résultats.
Cependant, je soupçonne que le multithreading sera plus rapide que le multitraitement en général, en particulier sous Windows, car Windows ne prend pas en charge la conversion, de sorte que chaque nouveau processus doit prendre du temps à se lancer. Sous Linux ou Mac, ils seront probablement plus proches.
Vous pouvez imbriquer plusieurs threads dans plusieurs processus, mais il est recommandé de ne pas utiliser plusieurs threads pour créer plusieurs processus.
from joblib import Parallel, delayed
import multiprocessing
inputs = range(10)
def processInput(i):
return i * i
num_cores = multiprocessing.cpu_count()
results = Parallel(n_jobs=num_cores)(delayed(processInput)(i) for i in inputs)
print(results)
Ce qui précède fonctionne à merveille sur ma machine (Ubuntu, le paquet joblib était pré-installé, mais peut être installé via pip install joblib
).
Tiré de https://blog.dominodatalab.com/simple-parallelization/
Il y a un certain nombre d'avantages à utiliser Ray :
Dans votre cas, vous pouvez démarrer Ray et définir une fonction distante.
import ray
ray.init()
@ray.remote(num_return_vals=3)
def calc_stuff(parameter=None):
# Do something.
return 1, 2, 3
puis invoquer en parallèle
output1, output2, output3 = [], [], []
# Launch the tasks.
for j in range(10):
id1, id2, id3 = calc_stuff.remote(parameter=j)
output1.append(id1)
output2.append(id2)
output3.append(id3)
# Block until the results have finished and get the results.
output1 = ray.get(output1)
output2 = ray.get(output2)
output3 = ray.get(output3)
Pour exécuter le même exemple sur un cluster, la seule ligne qui serait modifiée serait l'appel à ray.init (). La documentation pertinente peut être trouvée ici .
Notez que j'aide à développer Ray.
J'ai trouvé que joblib
est très utile avec moi. S'il vous plaît voir exemple suivant:
from joblib import Parallel, delayed
def yourfunction(k):
s=3.14*k*k
print "Area of a circle with a radius ", k, " is:", s
element_run = Parallel(n_jobs=-1)(delayed(yourfunction)(k) for k in range(1,10))
n_jobs = -1: utiliser tous les cœurs disponibles
pourquoi n'utilisez-vous pas de threads et un mutex pour protéger une liste globale?
import os
import re
import time
import sys
import thread
from threading import Thread
class thread_it(Thread):
def __init__ (self,param):
Thread.__init__(self)
self.param = param
def run(self):
mutex.acquire()
output.append(calc_stuff(self.param))
mutex.release()
threads = []
output = []
mutex = thread.allocate_lock()
for j in range(0, 10):
current = thread_it(j * offset)
threads.append(current)
current.start()
for t in threads:
t.join()
#here you have output list filled with data
gardez à l'esprit, vous serez aussi rapide que votre fil le plus lent
exemple très simple de traitement en parallèle est
from multiprocessing import Process
output1 = list()
output2 = list()
output3 = list()
def yourfunction():
for j in range(0, 10):
# calc individual parameter value
parameter = j * offset
# call the calculation
out1, out2, out3 = calc_stuff(parameter=parameter)
# put results into correct output list
output1.append(out1)
output2.append(out2)
output3.append(out3)
if __== '__main__':
p = Process(target=pa.yourfunction, args=('bob',))
p.start()
p.join()
Disons que nous avons une fonction asynchrone
async def work_async(self, student_name: str, code: str, loop):
"""
Some async function
"""
# Do some async procesing
Cela doit être exécuté sur un grand tableau. Certains attributs sont en train d'être transmis au programme et d'autres sont utilisés à partir de la propriété de l'élément dictionary du tableau.
async def process_students(self, student_name: str, loop):
market = sys.argv[2]
subjects = [...] #Some large array
batchsize = 5
for i in range(0, len(subjects), batchsize):
batch = subjects[i:i+batchsize]
await asyncio.gather(*(self.work_async(student_name,
sub['Code'],
loop)
for sub in batch))
Cela pourrait être utile lors de la mise en œuvre de l'informatique multitraitement et parallèle/distribuée en Python.
tutoriel YouTube sur l'utilisation du paquet techila
Techila est un middleware informatique distribué, qui s'intègre directement à Python à l'aide du package techila. La fonction Peach du package peut être utile pour la parallélisation des structures de boucle. (L'extrait de code suivant provient de Forums de la communauté Techila )
techila.Peach(funcname = 'theheavyalgorithm', # Function that will be called on the compute nodes/ Workers
files = 'theheavyalgorithm.py', # Python-file that will be sourced on Workers
jobs = jobcount # Number of Jobs in the Project
)
merci @iuryxavier
from multiprocessing import Pool
from multiprocessing import cpu_count
def add_1(x):
return x + 1
if __== "__main__":
pool = Pool(cpu_count())
results = pool.map(add_1, range(10**12))
pool.close() # 'TERM'
pool.join() # 'KILL'