Soumettre un grand nombre de jobs
Supposons qu'on veuille effectuer un grand nombre de travaux similaires, indépendants et séquentiels (ou faiblement parallèles).
Par exemple, executer le même programme (sequentiel) avec différents paramètres d'entrée, contenus dans des fichiers:
file_1
file_2
...
file_N
A titre d'exemple, supposons que l'exécutable my_program est purement séquentiel (utilise 1 seul coeur de calcul) et accepte les fichiers de ce type comme paramètre d'entrée.
Supposons aussi que N est assez grand et my_program assez long à exécuter - de sorte que le traitement séquentiel de ces N fichiers prendrait un temps important.
Plusieurs approches - chacune avec ses avantages et inconvénients - sont présentées ci-dessous.
Solution naive
Sauf pour un petit nombre de jobs (~20) il est fortement déconseillé d'utiliser cette méthode!
Nous pourrions écrire un script Slurm de type mono-coeur qui prend le fichier en entrée et le passe au programme my_program.
Par exemple
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=00:15:00
./my_program $1
Et ensuite soumettre N jobs à slurm
for i in {1..$N};do sbatch naive-manyjobs.slurm file_$i;done
Cette approche a le mérite d'être simple. Mais elle risque de saturer la file d'attente Slurm, ce qui ralentit le fonctionnement du gestionnaire de travaux et impacte negativement tous les utilisateurs connectés.
De plus, par cycle de fonctionnement Slurm considère au plus 50 jobs pour un éventuel lancement, et les jobs excédentaires ne gagnent pas en priorité en attendant.
L'approche est donc inefficace, à la fois en vue d'un traitement rapide des N fichiers, ainsi qu'en vue du fonctionnement global du cluster.
Job arrays
La fonctionnalité Job Array permet d'obtenir le même resultat en soumettant un seul script au gestionnaire de travaux. Par exemple (N=1000):
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=00:15:00
#SBATCH --array=1-1000%24
./my_program file_$SLURM_ARRAY_TASK_ID
L'option --array va créer un job array (une collection de jobs avec des paramètres identiques) et definir la variable d'environnement SLURM_ARRAY_TASK_ID (entre autres).
Ici un array de 1000 jobs est crée (1-1000), et le nombre de jobs simultanés est limité à 24 (%24).
Les fichiers s'appellent par defaut
slurm-${SLURM_ARRAY_JOB_ID}_${SLURM_ARRAY_TASK_ID}.out
Cette approche est plus efficace que la première.
Néanmoins la soumission de job-arrays très large peut surcharger le gestionnaire de travaux - par conséquent, la taille maximale d'un job-array est limité à 10000.
Background jobs
Pour éviter de saturer la file d'attente, il est possible de déplacer la boucle sur les fichiers à l'intérieur du script.
for i in {1..$N}; do ./my_program file_$i; done
Le problème évident est le traitement séquentiel des fichiers.
Une solution est d'écrire un script Slurm de type Multi-coeur et d'exécuter plusieurs my_program de manière concurrente sur les coeurs disponibles. Par exemple pour N=1000:
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=24
#SBATCH --time=12:00:00
N = 1000
for i in {1..$N}
do
./my_program file_${i} &
[[ $((i%$SLURM_CPUS_PER_TASK)) -eq 0 ]] && wait
done
Le signe & en ligne 11 cause l'exécution de my_program en arrière-plan (lancement non-bloquant) et la commande wait en ligne 12 (évoqué tous les SLURM_CPUS_PER_TASK itérations) bloque l'exécution du script jusqu'à ce que tous les jobs en arrière-plan soient complétés.
Cette approche n'est pas optimale car la durée d'exéction pour chaque paquet de SLURM_CPUS_PER_TASK tâches est déterminée par la tâche la plus longue.
GNU parallel
Une très bonne solution est d'utiliser l'outil GNU Parallel.
Cliquez ici pour afficher le tutoriel GNU Parallel.