Python exécuter un programme en parallèle un grand nombre de fois

Tout ce qui concerne la programmation.
Répondre
Avatar de l’utilisateur
Dunatotatos
Membre
Membre
Messages : 425
Inscription : 11 mai 2016, 20:56
Status : Hors-ligne

Salut à tous,

Je ne suis pas sûr que ce forum soit le meilleur endroit pour ma question, mais je tente quand même.
Je souhaite lancer un programme (un binaire présent sur ma machine) un grand nombre de fois, avec des arguments qui varient. Par "grand nombre", je veux dire ~1 000 000. J'utilise donc des `subprocess` pour les lancements de ces binaires.

J'ai un peu de traitement à faire sur ces arguments avant de les soumettre, alors j'utilise aussi multiprocess.Pool pour ces traitements, qui se chargent aussi de lancer le binaire.

L'architecture de mon application ressemble pour le moment à ça (avec un peu de pseudo-code)

Code : Tout sélectionner

def main():
    """Démarre un pool de processus pour traiter les arguments et lancer le binaire."""
    pool = Pool(NB_PROCESSES)
    pool.map_async(binary_wrapper, args)
    
def binary_wrapper(arg):
    """Transforme l'argument, puis lance mon binaire."""
    # Some stuff
    popen = subprocess.Popen((my_binary, arg))
    popen.wait()
Ce code fonctionne, mais il est sérieusement dégueulasse. Je vais démarrer un processus par argument pour lancer my_binary (ie, ~1M processus). Et en effet, lorsque mon application tourne, 2900% du CPU est utilisé par le système, et seulement 300% par l'utilisateur.
N'y a-t-il pas moyen d'utiliser un système équivalent à un multiprocess.Pool pour démarrer un programme indépendant ?

Merci d'avance pour votre aide !
Duna

EDIT: Je précise que je n'ai pas moyen de modifier le code du binaire. Je ne peux tripatouiller que des wrappers.
Avatar de l’utilisateur
Dunatotatos
Membre
Membre
Messages : 425
Inscription : 11 mai 2016, 20:56
Status : Hors-ligne

Il y a définitivement quelque-chose à faire. Si je lance mon binaire à l'aide de xargs, 2200% de la charge CPU est alloué à l'utilisateur, et seulement 300% au système.
Reste à comprendre la différence fondamentale entre xargs et mon script python.

EDIT : xargs reste tout de même très lent comparé à ce que j'espérai. J'ai l'impression de rater un détail important.
network-king
Membre
Membre
Messages : 165
Inscription : 09 nov. 2017, 11:23
Status : Hors-ligne

Je n'ai pas pris le temps de lire l'intégralité de tes deux messages mais as tu envisagé de faire tourner ton script dans un container en tant que Daemon?
Avatar de l’utilisateur
Dunatotatos
Membre
Membre
Messages : 425
Inscription : 11 mai 2016, 20:56
Status : Hors-ligne

network-king a écrit : 15 nov. 2017, 09:49 Je n'ai pas pris le temps de lire l'intégralité de tes deux messages mais as tu envisagé de faire tourner ton script dans un container en tant que Daemon?
Non, mais je ne vois pas trop quel est l'avantage d'une telle méthode. Une création plus rapide de processus ? La possibilité de les recycler ?
network-king
Membre
Membre
Messages : 165
Inscription : 09 nov. 2017, 11:23
Status : Hors-ligne

Tu parlais de légèreté
22% ou 2200%
3% ou 300% car je voudrais savoir comment tu peux avoir 2200% ou 300%
Avatar de l’utilisateur
Dunatotatos
Membre
Membre
Messages : 425
Inscription : 11 mai 2016, 20:56
Status : Hors-ligne

J'ai une machine avec 46 cœurs. 100% correspond à 1 cœur utilisé en totalité. Les pourcentages sont donc bien 2200% et 300%.

Ce que je cherche exactement, ce n'est pas de ne pas interrompre les actions de l'utilisateur. Cette machine est faite pour faire du calcul, eh bien qu'elle calcule. Ce qui m'intéresse, par contre, c'est qu'elle calcule, et non pas qu'elle passe son temps à faire des fork et des join de processus.
Avatar de l’utilisateur
vohu
Membre
Membre
Messages : 455
Inscription : 16 avr. 2016, 12:02
Localisation : Strasbourg
Status : Hors-ligne

Salut, tu sembles utiliser la lib threading, et à cause du GIL (https://wiki.python.org/moin/GlobalInterpreterLock), python ne peux pas vraiment exploiter le processeur comme on se l'imagine (En gros, le multithreading ne peut occuper chaque core à 100%) et il faut bien faire la différence entre asynchrone et parallélisme, voir la concurrence, puisque tu dis devoir faire des traitements avant d'éxécuter ce programme.

Lorsque le temps de traitement est allongé car on attends la réaction sur des entrées ou sorties, c'est de l'async. Ton programme ne fait qu'attendre une réponse... il n'utilise pas de ressources. Ton PC ne va pas être ralenti par ce traitement.
Si tu cherches à diviser une tâche, c'est du parallélisme. ton programme va devoir se partager la ressource qu'il dispose entre plusieurs fonctions. Ce genre de tâche impacte la réactivité de ta machine.
La concurrence, c'est le fait que plusieurs tâches se partagent une ressource commune (un fichier de donnée, une mémoire, un disque...)

Le problème dans ton besoin, c'est qu'on ne sait pas si ce qui est gourmand (pour chaque élément, durée définie par latences d'entrées/sorties (internet, disque dur,...), charge processeur... ??) :
- tes post-traitements
- ton programme externe

Une fois que tu sais le type de ressource fortement contribuée pour chaque cas, tu sais si tu dois utiliser/combiner les lib suivantes :
- multiprocessing (parallélisme au niveau des process obligatoire pour ne pas être bridé par le GIL, chaque core à 100%, puisque chaque tâche à son process) pour ce qui est ressource de calcul. Pour le nombre de worker à utiliser, je prends en général, le nombre de core - 1
- threads (asynchronisme) pour l'attente de réponse réseau. Le de worker à utiliser dépend de la ressource en attente.
- futures pour la concurence. Idem que les threads.


Je sais pas si ça t'aide vraiment ce que je viens de dire... c'est compliqué à expliquer et j'avais pas trop de temps :/
Avatar de l’utilisateur
piratebab
Site Admin
Site Admin
Messages : 4944
Inscription : 24 avr. 2016, 18:41
Localisation : sud ouest
Status : Hors-ligne

est ce que python est le langage le plus adapté pour ce que tu veux faire ?
je ne suis pas expert en la matière, mais je suis curieux.
Je lis souvent des trucs comme ça:
n est donc encore loin d’un langage optimisé pour les calculs massivement parallèles
https://www.stat4decision.com/fr/le-lan ... a-science/

Et il est loin dans les sondages
https://www.developpez.net/forums/d1084 ... parallele/

Si ton code à paralléliser est court et s'exécute rapidement, rien d'étonnant à ce que la machine passe son temps a gérer le parallélisme plutôt qu'a faire les calculs.
Ce serait aussi coté algorithme qu'il faudrait travailler.
Avatar de l’utilisateur
Dunatotatos
Membre
Membre
Messages : 425
Inscription : 11 mai 2016, 20:56
Status : Hors-ligne

Merci pour vos messages.

vohu > Non, j'utilise justement multiprocess pour éviter le blocage du GIL. Lis mon premier post ^^
piratebab > Je n'aurais pas naturellement choisi Python pour ce projet. Mais n'importe quel langage aurait posé le même problème, puisqu'il reste présent avec xargs.

J'ai fait un profiling de mon application, et 80% du temps est consommé par kallsyms, et en particulier par unmap_page_range et son pote dont je n'ai plus le nom sous la main. Le souci est donc bien la création des processus, et le même problème se présente avec n'importe quel langage.

J'ai résolu mon souci en ré-implémentant l'algorithme utilisé par ce binaire en Python. Ça m'a pris un temps considérable, mais le gain est significatif. de 30 heures de calcul, je passe à 15 minutes !

Quant au choix du langage pour faire du calcul parallèle, les résultats du sondage me surprennent. Vaut-il mieux passer 1h à écrire un code en Python qui utilise des bibliothèques fonctionnelles et efficaces (comme numpy, Tensorflow, ...), ou bien 1 semaine à écrire la même chose en C, from scratch (et certainement mois optimisé que des bibliothèques Python déjà existantes) ? Je ne suis pas expert dans tous les langages mentionnés dans le sondage. Mais clairement, choisir C et C++ en tête de liste me paraît absurde. Ce fil est un parfait exemple où un code propre en Python est plus rapide que son équivalent dégueulasse en C et sh (ou C et Python, ou même C tout court, puisque xargs est écrit en C).
Avatar de l’utilisateur
Mimoza
Contributeur
Contributeur
Messages : 655
Inscription : 22 avr. 2016, 12:00
Localisation : Terre
Status : Hors-ligne

Le sondage est un peu vieux … le Go et Rust aurait eu de bon score je pense.
Sinon tant mieux que tu ai pu résoudre ton soucis en réimplémentant l'algo.
J'aurais été toi j'aurais fait en sorte que ce soit le système qui gère le multi-threading, donc un script shell qui lance X fois le binaire.
Avatar de l’utilisateur
Dunatotatos
Membre
Membre
Messages : 425
Inscription : 11 mai 2016, 20:56
Status : Hors-ligne

Mimoza a écrit : 16 nov. 2017, 21:56 J'aurais été toi j'aurais fait en sorte que ce soit le système qui gère le multi-threading, donc un script shell qui lance X fois le binaire.
Ce n'est pas ce que j'ai fait à l'aide de xargs ? (Ce n'est pas sarcastique, mais une vraie question. Je ne vois pas de différence, mais ce peut être du à mon ignorance.)
Avatar de l’utilisateur
Mimoza
Contributeur
Contributeur
Messages : 655
Inscription : 22 avr. 2016, 12:00
Localisation : Terre
Status : Hors-ligne

Non en effet c'est ce que tu as fait … c'est moi qui ne connaissais pas xargs :003:
Répondre