J'ai écrit un petit script pour répartir la charge de travail entre 4 threads et pour tester si les résultats restent ordonnés (par rapport à l'ordre de l'entrée):
from multiprocessing import Pool
import numpy as np
import time
import random
rows = 16
columns = 1000000
vals = np.arange(rows * columns, dtype=np.int32).reshape(rows, columns)
def worker(arr):
time.sleep(random.random()) # let the process sleep a random
for idx in np.ndindex(arr.shape): # amount of time to ensure that
arr[idx] += 1 # the processes finish at different
# time steps
return arr
# create the threadpool
with Pool(4) as p:
# schedule one map/worker for each row in the original data
q = p.map(worker, [row for row in vals])
for idx, row in enumerate(q):
print("[{:0>2}]: {: >8} - {: >8}".format(idx, row[0], row[-1]))
Pour moi, cela se traduit toujours par:
[00]: 1 - 1000000
[01]: 1000001 - 2000000
[02]: 2000001 - 3000000
[03]: 3000001 - 4000000
[04]: 4000001 - 5000000
[05]: 5000001 - 6000000
[06]: 6000001 - 7000000
[07]: 7000001 - 8000000
[08]: 8000001 - 9000000
[09]: 9000001 - 10000000
[10]: 10000001 - 11000000
[11]: 11000001 - 12000000
[12]: 12000001 - 13000000
[13]: 13000001 - 14000000
[14]: 14000001 - 15000000
[15]: 15000001 - 16000000
Question: Donc, Pool
conserve-t-il vraiment l'ordre de l'entrée d'origine lors du stockage des résultats de chaque fonction map
dans q
?
Sidenote: Je pose cette question, car j'ai besoin d'un moyen simple de paralléliser le travail sur plusieurs travailleurs. Dans certains cas, la commande n'est pas pertinente. Cependant, dans certains cas, les résultats (comme dans q
) doivent être renvoyés dans l'ordre d'origine, car j'utilise une fonction de réduction supplémentaire qui s'appuie sur les données ordonnées.
Performance: Sur ma machine, cette opération est environ 4 fois plus rapide (comme prévu, car j'ai 4 cœurs) que l'exécution normale sur un seul processus. De plus, les 4 cœurs sont à 100% utilisés pendant l'exécution.
Pool.map
les résultats sont ordonnés. Si vous avez besoin de commande, c'est parfait; si vous ne le faites pas, Pool.imap_unordered
peut être une optimisation utile.
Notez que tandis que l'ordre dans lequel vous recevez les résultats de Pool.map
est fixe, l'ordre dans lequel ils sont calculés est arbitraire.
La documentation le facture comme "équivalent parallèle de la fonction intégrée map()
" . Puisque map
est garanti pour préserver l'ordre, multiprocessing.Pool.map
fait aussi cette garantie.