Aller au contenu principal

Introduction

L'outil GNU Parallel est installé sur les noeuds du cluster. Un tutorial très complet est disponible ici.

Nous nous limitons ici à une utilisation simple de GNU Parallel, avec l'objectif d'effectuer un grand nombre de tâches similaires, indépendants et séquentiels en parallèle.

Cas test

Supposons qu'on veuille traiter 100 fichiers nommés

file_001.txt
...
file_100.txt

avec un script dont le premier argument est le fichier d'entrée et le second le nom d'un fichier sortie

process_file
#!/bin/bash
infile=$1
outfile="out$infile"
sleep_secs=$(($RANDOM % 5 + 3))
echo "Write file $infile to $outfile and sleep for ${sleep_secs} seconds."
cat $infile > $outfile
sleep $sleep_secs

On peut générer ces fichiers avec une boucle

for i in {1..100}; do printf "%3d times %3d is %5d\n" "${i}" "${i}" "$(( ${i} * ${i} ))" > file_${i}.txt;done

et on verra dans un instant comment on aurait pu le faire avec la commande parallel!

Avant d'aller plus loin, n'oubliez pas de rendre process_file exécutable (chmod +x).

Traitement parallèle

Le traitement de ces 100 fichiers en parallèle peut se faire simplement avec le commande

parallel ./process_file file_{}.txt ::: {1..100}

Dans cette commande {} est remplacé par 1,2,...,100 et les 100 tâches résultantes sont exécutés en parallèle.

Avec l'option -a les arguments d'entrée peuvent aussi être donnés par un fichier :

ls file_*.txt > filelist
parallel -a filelist ./process_file

ou par l'entrée standard :

ls file*.txt | parallel ./process_file

Sources d'entrée multiples

Quand des sources multiples sont donnés, GNU parallel génère toutes les combinaisons possible :

parallel echo {} ::: A B C ::: 1 2

Sortie:

A 1
A 2
B 1
B 2
C 1
C 2

Ici {} est remplacé par tous les arguments d'entrée. Pour les accéder un par un:

parallel echo "{1} et {2}" ::: A B C ::: 1 2

Avec l'option --link il est possible de lier les arguments d'entrée:

parallel --link echo "{1} et {2}" ::: A B C ::: D E F

Sortie:

A D
B E
C F

Contrôler l'exécution

Avec l'option -j (ou --jobs) il est possible de spécifier le nombre de jobs concurrents. Par defaut, parallel utilisera tous les coeurs disponibles.

Dans la commande parallel le symbole {%} est remplacé par l'identifiant du fil d'exécution.

Par exemple

parallel -j 3 'echo hello {} from {%} && sleep 2s' ::: {1..6}

Sortie:

hello 1 from 1
hello 2 from 2
hello 3 from 3
hello 4 from 1
hello 5 from 2
hello 6 from 3

Il peut être intéressant de d'échelonner le démarrage des jobs, par exemple s'ils effectuent un nombre important d'opérations entrée-sortie. Exemple:

parallel --delay 3.5 'echo Starting {} on $(date)' ::: 1 2 3

Sortie:

Starting 1 on lun. 24 juil. 2023 14:26:52 CEST
Starting 2 on lun. 24 juil. 2023 14:26:56 CEST
Starting 3 on lun. 24 juil. 2023 14:26:59 CEST

GNU Parallel dans slurm

L'utilisation de GNU Parallel sur plusieurs noeuds est plus compliqué et ne sera pas abordé ici.

Pour soumettre un job GNU Parallel au gestionnaire de tâches il faudra donc utiliser un script de type multi-coeur

Monitoring de l'exécution

L'option --joblog permet de générer des statistiques simples sur les jobs déjà exécutés et permet de redemarrer un job qui n'a pas été terminé. Par exemple

parallel --joblog mylog sleep ::: 1 2 3 4 5 6

crée un fichier mylog qui commence par

Seq	Host	Starttime	JobRuntime	Send	Receive	Exitval	Signal	Command
1 : 1690206751.025 1.098 0 0 0 0 sleep 1
2 : 1690206751.026 2.002 0 0 0 0 sleep 2

Supposons que l'exécution ait été interrompu après un peu plus de 2 secondes et que le fichier mylog s'arrête là.

L'option --resume (avec les mêmes arguments d'entrée!) permet de continuer l'exécution:

parallel --resume --joblog mylog echo  ::: A B C D E F

Dans la sortie suivante notons les dates de démarrage des jobs 3,4,5,6 par rapport aux jobs 1 et 2:

Seq	Host	Starttime	JobRuntime	Send	Receive	Exitval	Signal	Command
1 : 1690206751.025 1.098 0 0 0 0 sleep 1
2 : 1690206751.026 2.002 0 0 0 0 sleep 2
3 : 1690206787.147 3.000 0 0 0 0 sleep 3
4 : 1690206787.148 4.002 0 0 0 0 sleep 4
5 : 1690206787.149 5.002 0 0 0 0 sleep 5
6 : 1690206787.150 6.001 0 0 0 0 sleep 6