4
votes

Comment paralléliser un programme Python sous Linux

J'ai une machine à 8 cœurs.

J'ai essayé d'utiliser le drapeau -parallel sur cette commande:

# filelist is the directory containing two file names, a.txt and b.txt.
# a.txt is the first file, b.xt is the second file
# i pass an .txt file with both the names to the main program

from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path
import sys

def translate(filename):
    print(filename)
    f = open(filename, "r")
    g = open(filename + ".x", , "w")
    for line in f:
        g.write(line)

def main(path_to_file_with_list):
    futures = []
    with ProcessPoolExecutor(max_workers=8) as executor:
        for filename in Path(path_to_file_with_list).open():
            executor.submit(translate, "filelist/" + filename)
        for future in as_completed(futures):
            future.result()

if __name__ == "__main__":
     main(sys.argv[1])

Mais je ne peut pas le faire fonctionner, c'est-à-dire que la question spécifique est: comment utiliser parallel dans bash avec une commande python sous Linux, ainsi que les arguments pour le cas spécifique mentionné ci-dessus.

Il existe une commande parallèle Linux ( sudo apt-get install parallel ), que j'ai lu quelque part peut faire ce travail mais je ne sais pas comment l'utiliser.

La plupart des ressources Internet expliquent comment faire il en python mais est-ce que cela peut être fait en bash?

Veuillez aider, merci.

Based on an answer, here is a working example that is still not working, please suggest how to make it work.

J'ai un dossier avec 2 fichiers, je veux juste créer leurs doublons avec un n différent ame parallèlement dans cet exemple.

python perfile_code.py list_of_files.txt


6 commentaires

pourquoi voter pour fermer? C'est une question très spécifique, demandant comment utiliser le parallèle dans bash avec python, avec des arguments. J'ai modifié la question pour la rendre plus claire, veuillez la reconsidérer.


Vous faites preuve d'un manque fondamental de connaissances sur le sujet du parallélisme. -parallel n'est pas une option de ligne de commande valide pour Python. La programmation d'opérations parallèles nécessite généralement des stratégies de développement proactives de la part du programmeur. Je suggérerais googler "python parallel".


@Ouroborus non, non, considérez ceci opensource.com/article/18/5/gnu-parallel Je veux exécuter un programme python avec ce parallèle..pour un cas très spécifique..si un programme de conversion arbitraire peut être redirigé vers le parallèle .. pourquoi pas un programme python?


Cela nécessite toujours que vous compreniez comment fonctionne le parallélisme en général et que votre logiciel soit capable de fonctionner dans cet environnement. Comme vous le décrivez, votre script python actuel ne bénéficierait pas de gnu parallel . Lire et comprendre l'article que vous avez lié vous aiderait grandement à comprendre ce que vous devez faire.


Il n'y a pas d'indicateur clé en main --parallel . Vous devez écrire le parallélisme vous-même voir: multiprocessing


Votre programme fonctionne-t-il si vous l'exécutez comme ceci cat list_of_files.txt | python perfile_code.py / dev / stdin ?


3 Réponses :


3
votes

D'après votre commentaire,

@Ouroborus non, non, considérez ceci opensource.com/article/18/5/gnu-parallel je veux exécuter un programme python avec ce parallèle..pour un cas très spécifique..si un programme de conversion arbitraire peut être piped to parallel .. pourquoi pas un programme python?

Je pense que cela pourrait aider:

convert n'a pas été choisi arbitrairement. Il a été choisi parce que c'est un programme plus connu qui mappe (grosso modo) un seul fichier d'entrée, fourni via la ligne de commande, à un seul fichier de sortie, également fourni via la ligne de commande.

Le shell typique for peut être utilisée pour parcourir une liste. Dans l'article que vous avez lié, ils montrent un exemple

find . -name "*jpeg" | parallel -I% --max-args 1 convert % %.png

Ceci (encore une fois, grosso modo) prend une liste de noms de fichiers et les applique, un par un, à un modèle de commande et puis exécute cette commande.

Le problème ici est que pour attendrait nécessairement qu'une commande soit terminée avant d'exécuter la suivante et pourrait donc sous-utiliser les processeurs multicœurs d'aujourd'hui.

parallel agit comme une sorte de remplacement de pour . Il suppose qu'une commande peut être exécutée plusieurs fois simultanément, chacune avec des arguments différents, sans que chaque instance n'interfère avec les autres.

Dans l'article, ils affichent une commande utilisant parallel code >

for i in *jpeg; do convert $i $i.png ; done

qui équivaut à la commande for précédente. La différence (encore approximative) est que parallel exécute simultanément plusieurs variantes de la commande basée sur un modèle sans nécessairement attendre que chacune se termine.


Pour votre situation spécifique, afin de pouvoir utiliser parallèle , vous devez:

  • Ajustez votre script python pour qu'il prenne une entrée (comme un nom de fichier) et une sortie (éventuellement aussi un nom de fichier), les deux via la ligne de commande.
  • Découvrez comment configurer parallèle afin qu'il puisse recevoir une liste de ces noms de fichiers à insérer dans un modèle de commande pour exécuter votre script python sur chacun de ces fichiers individuellement.


2 commentaires

pouvez-vous jeter un œil à l'exemple de travail que j'ai édité dans la question ci-dessus?


@Rafael Mis à part une erreur de syntaxe évidente, il semble qu'il devrait faire ce que vous attendez. Des tests rudimentaires montrent que cela fonctionne.



1
votes

Vous pouvez simplement utiliser une commande shell pour ordinaire et ajouter l'indicateur d'arrière-plan & à la commande python dans le pour :

from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path:

def translate(filename):
    ...

def main(path_to_file_with_list):
    futures = []
    with ProcessPoolExecutor(max_workers=8) as executor:
        for filename in Path(path_to_file_with_list).open():
            executor.submit(translate, filename)
        for future in as_completed(futures):
            future.result()

if __name__ == "__main__":
     import sys
     main(argv[1])

Bien sûr, en supposant que votre code python générera des sorties séparées par lui-même.

C'est aussi simple que cela. Bien que ce ne soit pas habituel - en général, les gens préféreront utiliser Python lui-même pour contrôler l'exécution parallèle de la boucle, si vous pouvez éditer le programme. Une bonne façon de faire est d'utiliser concurrent.futures en Python pour créer un pool de nœuds de calcul avec 8 nœuds de calcul - l'approche shell ci-dessus lancera toutes les instances en parallèle à la fois.

En supposant votre code a une fonction translate qui prend un nom de fichier, votre code Python pourrait être écrit comme suit:

for file in `cat list_of_files.txt`;
   do python perfile_code.py $file &
done

Cela ne dépendra pas d'un shell spécial syntaxe, et prend en charge les cas de coin et la gestion des nombres ou des travailleurs, ce qui pourrait être difficile à faire correctement à partir de bash.


5 commentaires

merci pour un exemple fonctionnel! J'ai essayé votre exemple, jetez un oeil à la question éditée, cela ne fonctionne toujours pas.


L'exemple que vous avez écrit fonctionne-t-il à la fois en python 3 et 2?


La seule partie Python3.5 + est le pathlib.Path . Dans Python 2.7, utilisez simplement l'ancien open (path_to_file_wiith_list) à la place, et pas besoin de depuis pathlib import Path . concurrent.futures fonctionne de la même manière dans Python 2.7 et les versions plus récentes.


Cela risque de surcharger votre machine. Si list_of_files.txt contient 1000000 noms, alors il est probable que votre machine ralentisse jusqu'à l'exploration.


La version Python crée un pool de nœuds de calcul. Les futurs objets en file d'attente utilisent un mini, um de ressources - même 1_000_000 noms sont des cacahuètes dans une machine avec 4 Go de mémoire principale. Bien sûr, 40_000_000 commencerait à être quelque chose - et si l'on avait cette grande liste de fichiers, il suffit de prendre soin de créer également les futurs - ce serait une question de 6 ou 7 LoC supplémentaires ci-dessus. La version shell oui, lance immédiatement tous les processus en parallèle - même quelques milliers de noms de fichiers submergeraient n'importe quelle machine.



1
votes

D'après votre question, la manière dont vous exécutez vos tâches en série n'est pas claire. Mais si nous supposons que vous exécutez:

parallel python perfile_code.py :::: filelist.txt

alors le moyen le plus simple de paralléliser cela serait:

parallel python perfile_code.py ::: file*

Si vous avez une liste de fichiers avec une ligne par fichier puis utilisez:

python perfile_code.py file1
python perfile_code.py file2
python perfile_code.py file3
:
python perfile_code.py fileN

Il exécutera un travail par thread du processeur en parallèle. Donc, si filelist.txt contient 1000000 noms, alors il ne les exécutera pas tous en même temps, mais ne démarrera un nouveau travail que lorsque l'un sera terminé.


0 commentaires