J'ai donc un algorithme que j'écris, et la fonction multiprocess
est censée appeler une autre fonction, CreateMatrixMp()
, sur autant de processus qu'il y a de cpus, en parallèle. Je n'ai jamais fait de multitraitement auparavant et je ne peux pas être sûr de laquelle des méthodes ci-dessous est plus efficace. Le mot "efficace" étant utilisé dans le contexte de la fonction CreateMatrixMp()
devant potentiellement être appelée des milliers de fois. J'ai lu toute la documentation sur le python multiprocessing
module, et sont arrivés à ces deux possibilités:
La première consiste à utiliser la classe Pool
:
def MatrixHelper(self, args):
return self.CreateMatrix(*args)
def Multiprocess(self, sigmaI, sigmaX):
cpus = mp.cpu_count()
print('Number of cpu\'s to process WM: %d' % cpus)
poolCount = cpus*2
args = [(sigmaI, sigmaX, i) for i in range(self.numPixels)]
pool = mp.Pool(processes = poolCount, maxtasksperchild= 2)
tempData = pool.map(self.MatrixHelper, args)
pool.close()
pool.join()
Et ensuite, utiliser la classe Process
:
def Multiprocess(self, sigmaI, sigmaX):
cpus = mp.cpu_count()
print('Number of cpu\'s to process WM: %d' % cpus)
processes = [mp.Process(target = self.CreateMatrixMp, args = (sigmaI, sigmaX, i,)) for i in range(self.numPixels)]
for p in processes:
p.start()
for p in processes:
p.join()
Pool
semble être le meilleur choix. J'ai lu que cela entraîne moins de frais généraux. Et Process
ne prend pas en compte le nombre de cpus sur la machine. Le seul problème est que l'utilisation de Pool
de cette manière me donne erreur après erreur, et chaque fois que j'en fixe un, il y en a un nouveau en dessous. Process
semble plus facile à implémenter, et pour autant que je sache, c'est peut-être le meilleur choix. Que vous dit votre expérience?
Si Pool
doit être utilisé, ai-je raison de choisir map()
? Il serait préférable que l'ordre soit maintenu. J'ai tempData = pool.map(...)
parce que la fonction map
est censée retourner une liste des résultats de chaque processus. Je ne sais pas comment Process
gère ses données renvoyées.
Je pense que la classe Pool
est généralement plus pratique, mais cela dépend si vous voulez que vos résultats soient ordonnés ou non.
Supposons que vous souhaitiez créer 4 chaînes aléatoires (par exemple, il pourrait s'agir d'un générateur d'ID utilisateur aléatoire):
import multiprocessing as mp
import random
import string
# Define an output queue
output = mp.Queue()
# define a example function
def Rand_string(length, output):
""" Generates a random string of numbers, lower- and uppercase chars. """
Rand_str = ''.join(random.choice(
string.ascii_lowercase
+ string.ascii_uppercase
+ string.digits)
for i in range(length))
output.put(Rand_str)
# Setup a list of processes that we want to run
processes = [mp.Process(target=Rand_string, args=(5, output)) for x in range(4)]
# Run processes
for p in processes:
p.start()
# Exit the completed processes
for p in processes:
p.join()
# Get process results from the output queue
results = [output.get() for p in processes]
print(results)
# Output
# ['yzQfA', 'PQpqM', 'SHZYV', 'PSNkD']
Ici, l'ordre n'a probablement pas d'importance. Je ne sais pas s'il y a une meilleure façon de le faire, mais si je veux garder une trace des résultats dans l'ordre dans lequel les fonctions sont appelées, je retourne généralement des tuples avec un ID comme premier élément, par exemple,
# define a example function
def Rand_string(length, pos, output):
""" Generates a random string of numbers, lower- and uppercase chars. """
Rand_str = ''.join(random.choice(
string.ascii_lowercase
+ string.ascii_uppercase
+ string.digits)
for i in range(length))
output.put((pos, Rand_str))
# Setup a list of processes that we want to run
processes = [mp.Process(target=Rand_string, args=(5, x, output)) for x in range(4)]
print(processes)
# Output
# [(1, '5lUya'), (3, 'QQvLr'), (0, 'KAQo6'), (2, 'nj6Q0')]
Cela me permet ensuite de trier les résultats:
results.sort()
results = [r[1] for r in results]
print(results)
# Output:
# ['KAQo6', '5lUya', 'nj6Q0', 'QQvLr']
Maintenant à votre question: en quoi est-ce différent de la classe Pool
? Vous préférez généralement Pool.map
pour retourner la liste ordonnée des résultats sans passer par le cercle de création de tuples et de les trier par ID. Ainsi, je dirais qu'il est généralement plus efficace.
def cube(x):
return x**3
pool = mp.Pool(processes=4)
results = pool.map(cube, range(1,7))
print(results)
# output:
# [1, 8, 27, 64, 125, 216]
De manière équivalente, il existe également une méthode "appliquer":
pool = mp.Pool(processes=4)
results = [pool.apply(cube, args=(x,)) for x in range(1,7)]
print(results)
# output:
# [1, 8, 27, 64, 125, 216]
Tous les deux Pool.apply
et Pool.map
verrouillera le programme principal jusqu'à la fin d'un processus.
Maintenant, vous avez également Pool.apply_async
et Pool.map_async
, qui renvoie le résultat dès que le processus est terminé, ce qui est essentiellement similaire à la classe Process
ci-dessus. L'avantage peut être qu'ils vous fournissent les fonctionnalités pratiques de apply
et map
que vous connaissez grâce à Python apply
et map
Vous pouvez facilement le faire avec pypeln :
import pypeln as pl
stage = pl.process.map(
CreateMatrixMp,
range(self.numPixels),
workers=poolCount,
maxsize=2,
)
# iterate over it in the main process
for x in stage:
# code
# or convert it to a list
data = list(stage)