Minimal & # 8220; File d'attente des tâches & # 8221; avec des outils Linux standard pour exploiter le processeur multicœur
-
06-07-2019 - |
Question
Quel est le moyen le plus simple et le plus simple de créer un système de file d'attente de tâches minimal pour Linux à l'aide d'outils bash et communs?
J'ai un fichier avec 9'000 lignes, chaque ligne a une ligne de commande bash, les commandes sont complètement indépendantes.
command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
...
Ma boîte a plus d'un cœur et je veux exécuter X tâches en même temps. J'ai cherché sur le Web un bon moyen de le faire. Apparemment, beaucoup de gens ont ce problème, mais personne n’a pour l’instant une bonne solution.
Il serait intéressant que la solution présente les fonctionnalités suivantes:
- peut interpréter plusieurs commandes (par exemple,
commande; commande
) - peut interpréter les redirections de flux sur les lignes (par exemple,
ls > /tmp/ls.txt
) - utilise uniquement les outils Linux courants
Bonus si cela fonctionne sur d'autres clones Unix sans exigences trop exotiques.
La solution
Pouvez-vous convertir votre liste de commandes en un Makefile? Si tel est le cas, vous pouvez simplement exécuter "make -j X".
Autres conseils
Parallèle GNU http://www.gnu.org/software/parallel/ est un outil plus général pour la parallélisation que PPSS.
Si le fichier d'exécution contient:
command 1 > Logs/1.log
command 2 > Logs/2.log
command 3 > Logs/3.log
vous pouvez faire:
cat runfile | parallel -j+0
qui exécutera une commande par cœur de processeur.
Si vos commandes sont aussi simples que ci-dessus, vous n'avez même pas besoin de runfile mais pouvez le faire:
seq 1 3 | parallel -j+0 'command {} > Logs/{}.log'
Si vous avez plus d’ordinateurs disponibles pour effectuer le traitement, consultez les options --sshlogin et --trc pour GNU parallèle.
D'accord, après avoir posé la question ici, j'ai trouvé le projet suivant qui semble prometteur: ppss .
Modifier: Ce n'est pas ce que je veux, PPSS se concentre sur le traitement de "tous les fichiers du répertoire A".
Eh bien, c’est quand même une question amusante.
Voici ce que je ferais, en supposant bash (1) bien sûr.
- déterminez combien de ces commandes peuvent utilement être exécutées simultanément. Ce ne sera pas juste le nombre de cœurs; beaucoup de commandes seront suspendues pour les E / S et ce genre de chose. Appelez ce numéro N.
N = 15
par exemple. - configurez un gestionnaire de signal d'interruption pour le signal SIGCHLD, qui se produit lorsqu'un processus enfant se termine.
trap signalHandler SIGCHLD
- cat votre liste de commandes dans un tuyau
- écrit une boucle qui lit stdin et exécute les commandes une par une en décrémentant un compteur. Lorsque le compteur est à 0, il
attend
s. - votre gestionnaire de signal, qui tourne sur SIGCHLD, incrémente ce compteur.
Alors maintenant, il lance les premières commandes N
, puis attend. Lorsque le premier enfant se termine, l'attente est renvoyée, il lit une autre ligne, exécute une nouvelle commande et attend à nouveau.
Maintenant, il s’agit d’un cas qui prend en charge de nombreux travaux se terminant de manière rapprochée. Je soupçonne que vous pouvez vous en tirer avec une version plus simple:
N=15
COUNT=N
cat mycommands.sh |
while read cmd
do
eval $cmd &
if $((count-- == 0))
then
wait
fi
od
Maintenant, celui-ci lancera les 15 premières commandes, puis exécutera les autres commandes une par une à la fin d'une commande.
Le script Mapreduce Bash est un plaisir similaire à l’informatique distribuée:
http://blog.last.fm/2009/04 / 06 / mapreduce-bash-script
Et merci d’avoir signalé les PPS!
Vous pouvez utiliser la commande xargs . Son - max-procs fait ce que vous voulez. Par exemple, la solution de Charlie Martin devient avec xargs:
tr '\012' '\000' <mycommands.sh |xargs --null --max-procs=$X bash -c
détails:
- X est le nombre de processus max. E.g: X = 15. --max-procs fait la magie
- le premier tr est ici pour terminer les lignes par des octets nuls pour l'option xargs --null afin que la redirection des guillemets, etc. ne soient pas développés à tort
- bash -c exécute la commande
Je l'ai testé avec ce fichier mycommands.sh, par exemple:
date
date "+%Y-%m-%d" >"The Date".txt
wc -c <'The Date'.txt >'The Count'.txt
Il s'agit d'un cas spécifique, mais si vous essayez de traiter un ensemble de fichiers et de générer un autre ensemble de fichiers de sortie, vous pouvez démarrer #cores le nombre de processus et vérifier s'il existe un fichier de sortie avant de le traiter. L'exemple ci-dessous convertit un répertoire de fichiers .m4b en fichiers .mp3:
Exécutez cette commande autant de fois que vous avez de cœurs:
ls * m4b | pendant la lecture f; faire le test -f $ {f% m4b} mp3 || mencoder-of rawaudio " $ f " -oac mp3lame -ovc copie -o $ {f% m4b} mp3; terminé &
Vous pouviez voir ma file de tâches écrite sur bash: https://github.com/pavelpat/yastq
Queue de tâche + Ajout en parallèle + dynamique
Utilisant une FIFO, ce script se lance lui-même pour traiter la file d'attente. De cette façon, vous pouvez ajouter des commandes à la file d'attente à la volée (lorsque la file d'attente est déjà lancée).
Utilisation: ./queue Commande [nbre d'enfants] [nom de la file d'attente]
Exemple, avec 1 thread:
./queue "sleep 5; echo ONE" ./queue "echo TWO"
Sortie:
ONE TWO
Exemple, avec 2 threads:
./queue "sleep 5; echo ONE" 2 ./queue "echo TWO"
Sortie:
TWO ONE
Exemple, avec 2 files d'attente:
./queue "sleep 5; echo ONE queue1" 1 queue1 ./queue "sleep 3; echo ONE queue2" 1 queue2
Sortie:
ONE queue2 ONE queue1
Le script (enregistrez-le sous forme de "file d'attente" et de file d'attente chmod + x):
#!/bin/bash #Print usage [[ $# -eq 0 ]] && echo Usage: <*> Command [# of children] [Queue name] && exit #Param 1 - Command to execute COMMAND="$1" #Param 2 - Number of childs in parallel MAXCHILD=1 [[ $# -gt 1 ]] && MAXCHILD="$2" #Param 3 - File to be used as FIFO FIFO="/tmp/defaultqueue" [[ $# -gt 2 ]] && FIFO="$3" #Number of seconds to keep the runner active when unused TIMEOUT=5 runner(){ #Associate file descriptor 3 to the FIFO exec 3"$FIFO" while read -u 3 -t $TIMEOUT line; do #max child check while [ `jobs | grep Running | wc -l` -ge "$MAXCHILD" ]; do sleep 1 done #exec in backgroud (eval "$line")& done rm $FIFO } writer(){ #fork if the runner is not running lsof $FIFO >/dev/null || (<*> "QueueRunner" "$MAXCHILD" "$FIFO" &) #send the command to the runner echo "$COMMAND" > $FIFO } #Create the FIFO file [[ -e "$FIFO" ]] || mkfifo "$FIFO" #Start the runner if in the runner fork, else put the command in the queue [[ "$COMMAND" == "QueueRunner" ]] && runner || writer