Account User Share LevelFS NormUsage GrpTRESUsed ------- ------ ------- --------- ----------- ----------- ... myproj myuser 1.00 *0.8532* 0.6543 cpu=10000 ...
Ce doc/cours est destiné aux utilisateurs ayant déjà une bonne pratique de SLURM et souhaitant approfondir leur compréhension pour optimiser leurs soumissions, maîtriser l’allocation fine des ressources, gérer efficacement les entrées/sorties à l’échelle et automatiser certaines tâches.
Chaque job que vous soumettez suit un parcours précis :
Soumission : Vous lancez . Le script est envoyé au contrôleur principal de SLURM (sbatch votre_script.sh ). SLURM vérifie la syntaxe de base des directives slurmctld #SBATCH et enregistre le job. Un JobID unique est retourné. A partir de ce moment là si vous étitez le script vos modifications ne sont plus prises en compte. SLURM a fait une copie de votre_script.sh et c’est cette copie qui sera utilisée. Pour autant certains attribut SLURM sont encore modifiables via la commande votre_script.sh .scontrol
Mise en Queue (Pending) : Le job est placé dans la file d’attente de la partition demandée (ou par défaut). Il est maintenant en état PENDING (PD). Il attend que les ressources demandées soient disponibles et que sa priorité soit suffisante.
Ordonnancement (Scheduling) : Le slurmctld évalue constamment la file d’attente. Il calcule la priorité de chaque job en attente et vérifie si les ressources nécessaires sont (ou seront bientôt) libres.
Allocation & Lancement : Lorsque les ressources sont disponibles et que le job est prioritaire, le slurmctld alloue les nœuds/CPU/mémoire/GPU nécessaires.
(Optionnel) Prolog : Avant que votre script ne démarre, SLURM exécute un script prolog (défini par l’administrateur) sur chaque nœud alloué. Utile pour préparer l’environnement (monter des systèmes de fichiers, vérifier l’état…).
Exécution du Script Batch : Votre script (votre_script.sh) est lancé sur le premier nœud alloué (ou selon la configuration). Il hérite des variables d’environnement SLURM (voir cours précédent). C’est ici que vous lancez vos srun, module load, etc. Le job passe à l’état RUNNING (R).
(Optionnel) Epilog : Une fois votre script terminé (avec succès ou en erreur), SLURM exécute un script epilog (défini par l’admin) sur chaque nœud alloué. Utile pour le nettoyage (démonter des FS, collecter des logs systèmes…).
Fin du Job : Le job se termine. Son état final est enregistré (COMPLETED, FAILED, TIMEOUT, CANCELLED…). Les ressources sont libérées. Les informations comptables sont envoyées à la base de données (slurmdbd).
Les scripts prolog et epilog sont gérés par les administrateurs du cluster. Leur contenu peut affecter le démarrage ou la fin de votre job (ex: un epilog long peut retarder légèrement la libération des nœuds).
|
SLURM n’est pas une simple file "Premier Arrivé, Premier Servi". Il utilise un système de priorités sophistiqué et des techniques pour maximiser l’utilisation du cluster. Vous ne pouvez pas agir sur la priorité de vos jobs. Ces explications sont données pour vous aider à comprendre pourquoi vos jobs ne tournent pas instanément ou pourquoi des jobs d’autre utilsateurs "vous passe devant".
Facteurs de Priorité : Plusieurs éléments influencent la priorité d’un job en attente :
Âge (Age) : Depuis combien de temps le job attend. Plus il attend, plus sa priorité augmente.
Taille du Job (Job Size) : Le nombre de nœuds/CPU demandés. SLURM peut légèrement favoriser les petits jobs pour améliorer le flux, mais c’est souvent contrebalancé par d’autres facteurs.
Fair-share : Très important ! Tient compte de l’utilisation passée du cluster par l’utilisateur et ses groupes d’appartenance. Si vous avez beaucoup utilisé le cluster récemment, votre priorité "fair-share" diminue temporairement pour laisser la place aux autres. Inversement, si vous avez peu utilisé, votre priorité augmente.
Vérifiez votre score : (si autorisé). Cherchez sshare -l -u $USER LevelFS (un score > 1 est bon, < 1 moins bon).
.Exemple de sortie sshare -l -u $USER (simplifié)
Account User Share LevelFS NormUsage GrpTRESUsed ------- ------ ------- --------- ----------- ----------- ... myproj myuser 1.00 *0.8532* 0.6543 cpu=10000 ...
Ici, LevelFS est inférieur à 1, indiquant une priorité fair-share légèrement réduite due à une utilisation récente.
QOS (Quality of Service) : Des "niveaux de service" définis par les admins qui peuvent affecter directement la priorité (via un PriorityWeight) et les limites (temps max, nombre de jobs max…). Vous pouvez demander une QOS spécifique avec --qos=<nom_qos>.
Partition : Chaque partition peut avoir un facteur de priorité (PriorityTier). Les partitions "debug" ont souvent une priorité plus élevée.
Backfilling : C’est une technique clé qui permet à SLURM de lancer des jobs de plus basse priorité (souvent plus petits et plus courts) avant un job de haute priorité en attente, à condition que cela ne retarde pas le démarrage estimé du job prioritaire.
Scénario concret :
Job A (gros, prioritaire) demande 10 nœuds pour 24h. Il est en attente (PD) car seulement 8 nœuds sont libres. SLURM estime qu’il démarrera dans 2 heures quand 2 nœuds supplémentaires se libéreront.
Vous soumettez Job B (petit, moins prioritaire) demandant 1 nœud pour 1 heure.
SLURM calcule : "Job B peut tourner sur l’un des 8 nœuds libres et finir en 1 heure, bien avant que Job A ne puisse démarrer dans 2 heures".
Résultat : SLURM lance Job B immédiatement via backfilling, même s’il est moins prioritaire que Job A. Cela améliore l’utilisation globale du cluster.
sprio Quand votre job est en PD (Pending), vous donne la raison principale (ex: squeue -j <jobid> Resources, Priority, Dependency). Pour une analyse plus fine des facteurs de priorité, utilisez .sprio
Syntaxe :
: Affiche les facteurs pour tous vos jobs en attente.sprio
: Affiche les facteurs pour un job spécifique.sprio -j <job_id>
Interpréter la Sortie :
décompose la priorité totale en plusieurs composantes. Plus le nombre est élevé, plus ce facteur contribue positivement à la priorité.sprio
sprio -j 12345JOBID PARTITION PRIORITY SITE AGE FAIRSHARE JOBSIZE PARTITION QOS NICE TRES 12345 compute *56789* 0 *5000* *1234* *500* *50* *0* 0 -
Explications :
PRIORITY : La priorité numérique totale du job (le plus élevé est lancé en premier, à ressources égales).
AGE : Contribution de l’ancienneté du job dans la file. Augmente avec le temps.
FAIRSHARE : Contribution du score fair-share. Peut être bas si vous avez beaucoup utilisé le cluster récemment.
JOBSIZE : Contribution de la taille du job (souvent moins impactant).
PARTITION : Contribution de la priorité de la partition demandée.
QOS : Contribution de la Qualité de Service demandée (si applicable).
Les autres colonnes (SITE, NICE, TRES) sont pour des facteurs plus spécifiques ou moins courants.
Analyse : En regardant quels facteurs sont les plus élevés ou les plus bas, vous comprenez mieux ce qui influence la position de votre job dans la file. Si FAIRSHARE est très bas, il n’y a pas grand-chose à faire à part attendre que votre score remonte. Si tous les facteurs sont élevés mais que le job attend toujours (raison Resources dans squeue), c’est simplement que le cluster est très chargé et qu’il n’y a pas assez de ressources libres pour votre demande.
Allons plus loin que les simples demandes --nodes, --ntasks, --mem, --time.
Parfois, un workflow nécessite différentes étapes avec différents besoins matériels au sein de la même allocation de job. On utilise alors srun pour lancer des "job steps" distincts avec des ressources spécifiques.
Principe : sbatch demande l’ensemble des ressources nécessaires pour toutes les étapes. Ensuite, chaque srun dans le script spécifie les ressources qu’il utilise pour son étape particulière, piochant dans l’allocation globale.
Option Clé : --exact (pour srun) est souvent utilisé pour s’assurer qu’une étape n’utilise que les cœurs spécifiés pour elle, même si plus de cœurs sont alloués au job global.
Option Clé GPU : --gpus ou --gres=gpu:… dans srun pour spécifier quels/combien de GPU sont utilisés par cette étape (parmi ceux alloués par sbatch).
#!/bin/bash
# Allocation globale : 1 noeud, 16 coeurs CPU, 64Go RAM, 2 GPUs, pendant 2h
#SBATCH --nodes=1
#SBATCH --ntasks=16 # Total CPU cores requested - can be oversubscribed by steps
#SBATCH --cpus-per-task=1 # Easier to manage with srun specifying tasks/cpus
#SBATCH --mem=64G
#SBATCH --gres=gpu:2 # Demande 2 GPUs pour l'ensemble du job
#SBATCH --time=02:00:00
#SBATCH --job-name=CPU_then_GPU
#SBATCH --output=hetero_%j.out
#SBATCH --error=hetero_%j.err
echo "--- Job Hétérogène Démarré (ID: $SLURM_JOB_ID) ---"
echo "Allocation: $SLURM_NNODES noeud(s), $SLURM_NTASKS tâches/coeurs max, $SLURM_MEM_PER_NODE Mo, $SLURM_GPUS GPU(s)"
echo "GPU IDs alloués au job: $CUDA_VISIBLE_DEVICES" # Voir tous les GPUs alloués
# --- Etape 1: Pré-traitement CPU utilisant 8 coeurs ---
echo " "
echo "=== Etape 1 : Lancement Pré-traitement CPU ==="
# On lance 1 tâche qui utilisera 8 coeurs CPU.
# --exact garantit que srun n'utilise que ces 8 coeurs.
# Pas besoin de --gres ici car cette étape n'utilise pas de GPU.
srun --nodes=1 --ntasks=1 --cpus-per-task=8 --exact \
./mon_pretraitement --input data_brute.dat --output data_traitee.bin --threads 8
echo "=== Etape 1 : Pré-traitement CPU Terminé ==="
# --- Etape 2: Calcul GPU utilisant 2 processus, chacun sur 1 GPU et 4 CPUs ---
echo " "
echo "=== Etape 2 : Lancement Calcul GPU (2 processus) ==="
# On lance 2 tâches. Chaque tâche utilisera 4 coeurs CPU et 1 GPU.
# Total: 2*4=8 CPUs utilisés pour cette étape, 2 GPUs utilisés.
# SLURM s'occupe de distribuer les 2 GPUs alloués (via --gres=gpu:2 dans SBATCH)
# entre les 2 tâches demandées (via --gpus-per-task=1 dans srun).
srun --nodes=1 --ntasks=2 --cpus-per-task=4 --exact \
--gpus-per-task=1 \
./mon_calcul_gpu --input data_traitee.bin --output resultat_final.res
# Note: L'option --gpu-bind=... peut être ajoutée pour contrôler finement
# quel processus va sur quel GPU si nécessaire.
echo "=== Etape 2 : Calcul GPU Terminé ==="
echo "--- Job Hétérogène Fini ---"
--constraint / -C)Ciblez précisément le matériel dont vous avez besoin ! Utile si votre code est optimisé pour une architecture CPU spécifique, ou nécessite un type de réseau particulier.
Trouver les Caractéristiques Disponibles (features) :
sinfo -o "%N %f" # Affiche les features pour chaque noeud
# Output Exemple:
# HOSTNAMES FEATURES
# io-CPU-001 cpu_amd,epyc,avx2,ib
# io-mem-002 cpu_intel,avx512,ib
# io-GPU-001 cpu_intel,gpu,a100
# io-GPU-010 cpu_amd,epic,gpu,H100,nvlink
Les features sont définies par les administrateurs.
Syntaxe de la Contrainte :
-C <feature> : Demande des nœuds ayant cette caractéristique.
-C "<feature1>&<feature2>"` : Demande des nœuds ayant les deux caractéristiques (ET logique).
-C "<feature1>|<feature2>"` : Demande des nœuds ayant l’une ou l’autre caractéristique (OU logique).
-C "[feature_prefix*]"` : Utilisation de jokers (peut dépendre de la config Slurm).
-C "feature1&!feature2"` : Demande feature1 mais PAS feature2 (NON logique).
Exemples Concrets :
# Demander un noeud avec support AVX-512 et réseau InfiniBand
#SBATCH --constraint="avx512&ib"
# Demander un noeud avec un CPU AMD Epyc (si feature 'Epic' existe)
#SBATCH -C "epyc"
# Demander un noeud GPU H100 avec NVLink (si features 'H100' et 'nvlink' existent)
#SBATCH --partition=gpu # Probablement nécessaire aussi XXXXXX
#SBATCH -C "H100&nvlink"
# Demander un noeud soit EPYC soit Skylake
#SBATCH -C "epyc|skylake"
| L’utilisation de contraintes peut augmenter votre temps d’attente si peu de nœuds correspondent. Ne les utilisez que si c’est réellement nécessaire pour la performance ou la compatibilité. |
Ces mécanismes déterminent l’accès prioritaire et les limites au-delà des partitions.
QOS (Quality of Service) :
Définit un ensemble de limites et/ou un poids de priorité. Un utilisateur/groupe peut avoir accès à une ou plusieurs QOS.
Exemples de QOS (hypothétiques) :
qos_debug: MaxWall=1:00:00, MaxJobs=5, Priority=10000
qos_normal: MaxWall=7-00:00:00, MaxJobs=200, Priority=1000 (par défaut)
qos_project_x: MaxWall=30-00:00:00, MaxJobs=500, Priority=5000, accès à une partition réservée.
qos_lowprio: MaxWall=15-00:00:00, MaxJobs=100, Priority=100 (pour les jobs non urgents).
Voir les QOS disponibles et vos accès : (si autorisé).sacctmgr show assoc user=$USER
Demander une QOS spécifique : ```bash # Soumettre avec une QOS spécifique pour un temps plus long ou une priorité différente
#SBATCH --qos=long_jobs
# Utiliser une QOS basse priorité pour un job non urgent
#SBATCH --qos=lowprio
| Utiliser une QOS à haute priorité peut nécessiter une justification ou être limité à certains projets. |
Fairshare :
Rappel : Influence la priorité à l’intérieur d’une partition/QOS. Basé sur l’utilisation récente.
Voir son score : (chercher sshare -l -u $USER LevelFS).
Voir l’arbre Fairshare (si autorisé) : (montre la hiérarchie comptes/utilisateurs et leur utilisation/score).sshare -l
Impact : Si votre LevelFS est bas (<1), vos jobs auront une priorité réduite par ce facteur. Si LevelFS est haut (>1), ils auront un boost. Le score revient vers 1 avec le temps si vous n’utilisez pas le cluster.
Stratégie : On ne peut pas directement "booster" son score fairshare, mais être conscient de son niveau aide à comprendre les temps d’attente. Éviter de soumettre des milliers de jobs inutiles peut aider à maintenir un bon score à long terme.
Souvent négligé, l’I/O peut devenir le goulot d’étranglement principal, surtout pour les jobs parallèles ou manipulant de gros volumes de données.
Contention : Des centaines de nœuds accédant au même système de fichiers partagé en même temps.
Latence vs Bande Passante : Accéder à des métadonnées (ouvrir un fichier, lister un répertoire) est sensible à la latence. Lire/écrire de gros volumes est sensible à la bande passante.
Goulot d’Étranglement des Métadonnées : Créer ou accéder à des millions de petits fichiers dans un seul répertoire peut saturer les serveurs de métadonnées (MDS).
Systèmes de Fichiers Parallèles (PFS) : Puissants mais complexes (Weka, anciennement Lustre et GPFS).
Comment votre application lit/écrit les données a un impact énorme.
Fichier par Processus : Simple à implémenter (chaque rang MPI écrit resultat_rang_N.dat). Inconvénients : Peut créer trop de fichiers, difficile à agréger, peut saturer les MDS si N est grand.
Fichier Partagé : Tous les processus écrivent dans le même fichier. Nécessite une coordination pour éviter que les processus n’écrivent les uns sur les autres. C’est là qu’interviennent MPI-IO, HDF5, NetCDF.
I/O Indépendant vs Collectif (pour fichier partagé) :
Indépendant : Chaque processus calcule où il doit écrire et écrit directement. Peut entraîner des accès disque non contigus et inefficaces.
Collectif : Les processus se coordonnent (via MPI-IO ou la bibliothèque) pour regrouper les écritures/lectures en blocs plus larges et plus contigus. Généralement beaucoup plus performant.
Simplifient l’I/O parallèle en masquant la complexité de MPI-IO et en ajoutant des fonctionnalités (auto-description, portabilité…).
HDF5 (Hierarchical Data Format 5) / NetCDF (Network Common Data Form 4 - souvent basé sur HDF5) :
Stockent les données dans des structures hiérarchiques (groupes, datasets) avec métadonnées.
Supportent la lecture/écriture parallèle si compilés avec MPI.
Activation du Parallélisme (Conceptuel) : Il faut typiquement passer le communicateur MPI et utiliser des propriétés spécifiques lors de l’ouverture/création du fichier et des transferts de données.
ADIOS (Adaptable Input/Output System) :
Axé sur la performance et la flexibilité des "moteurs" d’écriture/lecture (BPFile, HDF5, SST pour streaming…).
Découple l’écriture de la lecture (peut réorganiser les données à l’écriture pour optimiser la lecture).
Nécessite d’instrumenter le code avec l’API ADIOS.
Pour gérer des campagnes de simulation ou des analyses complexes, l’automatisation devient nécessaire.
On peut créer des scripts "wrapper" qui pilotent SLURM.
Parser la Sortie pour le Job ID : Essentiel pour créer des dépendances ou suivre un job.
#!/bin/bash
JOB_SCRIPT="mon_calcul.sh"
SBATCH_ARGS="--time=1:00:00 --mem=4G" # Arguments pour sbatch
echo "Soumission de $JOB_SCRIPT avec args: $SBATCH_ARGS"
# Utiliser --parsable est souvent le plus simple, sinon il faut filtrer la sortie standard
# sbatch --parsable $SBATCH_ARGS "$JOB_SCRIPT" peut ne renvoyer que l'ID
# Sinon, capturer la sortie standard:
S_OUT=$(sbatch $SBATCH_ARGS "$JOB_SCRIPT")
STATUS=$? # Code de retour de sbatch
if [ $STATUS -ne 0 ]; then
echo "ERREUR: La soumission sbatch a échoué avec le code $STATUS !"
echo "Sortie de sbatch: $S_OUT"
exit $STATUS
fi
# Extraire l'ID (suppose le format "Submitted batch job XXXXX")
JOBID=$(echo "$S_OUT" | awk '/Submitted batch job/ {print $NF}')
if ! [[ "$JOBID" =~ ^[0-9]+$ ]]; then
echo "ERREUR: Impossible d'extraire le Job ID de la sortie: $S_OUT"
exit 1
fi
echo "Job soumis avec succès. ID: $JOBID"
# On peut maintenant utiliser $JOBID pour créer des dépendances, etc.
# Exemple: soumettre un job dépendant
# sbatch --dependency=afterok:$JOBID post_traitement.sh
Monitorer un Job dans un Script : Attendre qu’un job soit terminé. .Exemple Bash pour attendre la fin d’un job
#!/bin/bash
JOBID_TO_WAIT_FOR="$1" # Prend le Job ID en argument
if ! [[ "$JOBID_TO_WAIT_FOR" =~ ^[0-9]+$ ]]; then
echo "Usage: $0 <JobID>"
exit 1
fi
echo "Attente de la fin du Job ID: $JOBID_TO_WAIT_FOR"
while true; do
# Utiliser sacct pour obtenir l'état final (plus fiable que squeue pour les jobs très courts)
# -n : pas d'en-tête, -o State%15 : format état sur 15 chars
# head -n 1 : prend la première ligne (le job principal, pas les steps)
# xargs : enlève les espaces superflus
STATE=$(sacct -j "$JOBID_TO_WAIT_FOR" -n -o State%15 | head -n 1 | xargs)
# Vérifier si l'état indique une fin (ou si le job n'existe plus dans sacct)
case "$STATE" in
COMPLETED|FAILED|CANCELLED|TIMEOUT|NODE_FAIL|PREEMPTED|"")
echo "Job $JOBID_TO_WAIT_FOR terminé avec l'état final: '$STATE'"
# Sortir de la boucle si l'état est terminal ou vide
break
;;
*)
# Afficher l'état courant (pour info) et attendre
echo "Job $JOBID_TO_WAIT_FOR est dans l'état: '$STATE'. Attente..."
sleep 30 # Attendre 30 secondes avant de vérifier à nouveau
;;
esac
done
# Optionnel: vérifier l'état final et agir en conséquence
if [ "$STATE" == "COMPLETED" ]; then
echo "Job réussi. Lancement de l'étape suivante..."
# sbatch --dependency=... # Ou autre action
exit 0
else
echo "Job échoué ou annulé (état: $STATE)."
exit 1
fi
Extraire des Données avec sacct (Format Parsable) : --parsable2 utilise | comme séparateur, --noheader supprime l’en-tête. Très pratique pour cut, awk, etc. Exemple :
# Obtenir le MaxRSS (en Ko) et l'ExitCode pour une liste de jobs
JOB_IDS="12345,12346,12347"
sacct -j "$JOB_IDS" --parsable2 --noheader -o JobID,MaxRSS,ExitCode | while IFS='|' read -r id rss exitcode; do
# Enlever le 'K' de MaxRSS si présent
rss_kb=${rss%K}
echo "Job: $id MaxRSS(KB): $rss_kb ExitCode: $exitcode"
done
Utiliser scontrol pour Lire/Modifier (Attention !) :
# Obtenir l'état d'un job
JOB_STATE=$(scontrol show job $JOBID | awk -F'=' '/JobState=/{print $2}' | cut -d' ' -f1)
echo "Etat actuel du job $JOBID: $JOB_STATE"
# Augmenter la limite de temps (si job RUNNING et autorisé)
NEW_TIMELIMIT="04:00:00"
echo "Tentative de mise à jour de TimeLimit pour $JOBID à $NEW_TIMELIMIT"
scontrol update JobId=$JOBID TimeLimit=$NEW_TIMELIMIT
# Vérifier si la mise à jour a réussi (nécessite plus de logique)