8
votes

Fonction récursive parallèle en python

Comment puis-je paralléLiser une fonction récursive dans Python?

Ma fonction ressemble à ceci: xxx

Lorsque vous essayez de le paralléléter avec multiprocessing.pool. mappe , Windows ouvre un nombre infini de processus et suspends.

Qu'est-ce qu'un bon moyen (de préférence simple) de le parallementer (pour une seule machine multicœur)?

ici est le code qui est suspendu: xxx


2 commentaires

Quel est le type de retour de f ? Il semble que cela renvoie une liste. Mais quel est le type d'élément de liste? C'est ce que je suis bloqué (code similaire à la réponse ci-dessous). Il semble que cela puisse être une liste de listes? Et chacun de ceux-ci peut être une liste? ou est quelque chose qui ne va pas ci-dessus?


X est une feuille d'arbre. Le type de retour est un arbre, dans la notation Lisp [tête, sous-arbres])


3 Réponses :


7
votes

OK, désolé pour les problèmes avec cela.

Je vais répondre à une question légèrement différente où f () renvoie la somme des valeurs de la liste. C'est-à-dire parce que ce n'est pas clair pour moi de votre exemple quel type de retour de f () serait, et l'utilisation d'un entier rend le code simple à comprendre.

Ceci est complexe Parce que deux choses différentes se passent en parallèle:

  1. Le calcul de la fonction coûteuse dans la piscine
  2. L'expansion récursive de f ()

    Je suis très prudent d'utiliser uniquement la piscine pour calculer la fonction coûteuse. De cette façon, nous ne recevons pas une "explosion" de processus, mais parce que cela est asynchrone, nous devons reporter un lot de travail pour le rappel que le travailleur appelle une fois la fonction coûteuse terminée. < / p>

    Plus que cela, nous devons utiliser un loquet de compte à rebours afin que nous sachions quand tous les appels distincts sur f () sont terminés.

    là Peut être un moyen plus simple (je suis à peu près sûr qu'il y a, mais je dois faire d'autres choses), mais cela vous donne peut-être une idée de ce qui est possible: xxx

    ps : J'utilise Python® 3.2 et la laideur ci-dessus est parce que nous retardons le calcul des résultats finaux (sauvegarder l'arborescence) jusqu'à plus tard. Il est possible que quelque chose comme des générateurs ou des contrats à terme pourrait simplifier les choses.

    En outre, je suppose que vous avez besoin d'un cache pour éviter de recalculer inutilement la fonction coûteuse lorsqu'il est appelé avec le même argument que plus tôt.

    Voir aussi La réponse de Yaniv - qui semble être une solution alternative d'inverser la ordre de l'évaluation en étant explicite de profondeur.


8 commentaires

1. Définissez la piscine à l'intérieur de la fonction. 2. Avant que la ligne mentionnée, calculez la liste_fralues ​​à l'aide de s'appliquant. 3. Fermez la piscine.


intéressant. Merci pour l'info. Aller jouer avec cela moi-même ...: o)


Andrew pourriez-vous s'il vous plaît expliquer comment la piscine.Appliquez-vous? Parce que cela ne semble pas quelque chose que vous pouvez paraller: appliquer une fonction à un seul argument. Mais quand je l'essaie (même avec une piscine (processus = 1)), je reçois d'excellents résultats.


Je ne suis pas sûr, maintenant. Je peux expliquer ce que Pool.Prappy fait-il juste la fonction ailleurs - mais je ne peux pas expliquer pourquoi cela aide ici (et je pense que ma réponse est fausse). Je me lève avec une œuvre de mienne, mais je reviendrai à cette réponse et je reviendrai ma réponse quand je comprends plus (probablement dans les 12 prochaines heures).


Merci!!. Je vais aussi faire la recherche cela.


Après avoir creusé un peu plus de choses dessus: parce que j'ai fait tous mes tests dans le débogueur de l'IDE, et la piscine fonctionne en dehors du débogueur, c'était plus rapide. Mais dans la comparaison de la vie réelle, ils donnent une heure similaire.


Ok, j'ai mis à jour la réponse et j'appelle cela par jour ... J'espère que ça vous aide. Je suppose qu'il y a une solution beaucoup plus élégante, mais c'est tout ce que je pouvais trouver rapidement.


Andrew, puisque votre solution est complète, elle devrait être la réponse définitive. Mais s'il vous plaît lien vers ma solution partielle mais plus simple ci-dessous.



3
votes

Après avoir pensé à cela, j'ai trouvé une réponse simple simple, pas complète mais assez bonne: xxx


0 commentaires

1
votes

Je stocke l'identifiant de processus principal initialement et transférez-le aux sous-programmes.

Lorsque j'ai besoin de démarrer un emploi multiprofessionnel, je vérifie le nombre d'enfants du processus principal. S'il est inférieur ou égal à la moitié de mon comptage de la CPU, je l'exécute comme parallèle. Si cela est supérieur à la moitié de mon comptage de la CPU, je l'exécute en série. De cette manière, cela évite les goulots d'étranglement et utilise efficacement les cœurs de la CPU. Vous pouvez régler le nombre de cœurs pour votre cas. Par exemple, vous pouvez le définir sur le nombre exact de cœurs CPU, mais vous ne devez pas le dépasser. P>

def subProgramhWrapper(func, args):
    func(*args)

parent = psutil.Process(main_process_id)
children = parent.children(recursive=True)
num_cores = int(multiprocessing.cpu_count()/2)

if num_cores >= len(children):
    #parallel run
    pool = MyPool(num_cores)
    results = pool.starmap(subProgram, input_params)
    pool.close()
    pool.join()
else:
    #serial run
    for input_param in input_params:
        subProgramhWrapper(subProgram, input_param)


0 commentaires