Jean Zay : exécution d'un travail multi GPU MPI en batch

Les travaux sont gérés sur l'ensemble des nœuds par le logiciel Slurm.

Pour soumettre un travail distribué multi GPU MPI en batch sur Jean Zay, il faut créer un script de soumission en vous inspirant des deux exemples donnés ci-dessous.

Si votre code requiert la fonctionnalité MPI CUDA-aware, vous devez vous référer à la page exécution d'un travail multi GPU MPI CUDA-aware et GPUDirect en batch.

  • Pour une exécution utilisant 3 GPU, sur un même nœud, de la partition gpu par défaut :

    multi_gpu_mpi.slurm
    #!/bin/bash
    #SBATCH --job-name=gpu_multi_mpi     # nom du job
    # Il est possible d'utiliser une autre partition que celle par défaut
    # en activant l'une des 5 directives suivantes :
    ##SBATCH -C v100-16g                 # decommenter pour reserver uniquement des GPU V100 16 Go
    ##SBATCH -C v100-32g                 # decommenter pour reserver uniquement des GPU V100 32 Go
    ##SBATCH --partition=gpu_p2          # decommenter pour la partition gpu_p2 (GPU V100 32 Go)
    ##SBATCH -C a100                     # decommenter pour la partition gpu_p5 (GPU A100 80 Go)
    ##SBATCH -C h100                     # decommenter pour la partition gpu_p6 (GPU H100 80 Go)
    # Ici, reservation de 3x10=30 CPU (pour 3 taches) et de 3 GPU (1 GPU par tache) sur un seul noeud :
    #SBATCH --nodes=1                    # nombre de noeud
    #SBATCH --ntasks-per-node=3          # nombre de tache MPI par noeud (= nombre de GPU par noeud)
    #SBATCH --gres=gpu:3                 # nombre de GPU par nœud (max 8 avec gpu_p2, gpu_p5)
    # Le nombre de CPU par tache doit etre adapte en fonction de la partition utilisee. Sachant
    # qu'ici on ne reserve qu'un seul GPU par tache (soit 1/4 ou 1/8 des GPU du noeud suivant
    # la partition), l'ideal est de reserver 1/4 ou 1/8 des CPU du noeud pour chaque tache:
    #SBATCH --cpus-per-task=10           # nombre de CPU par tache (1/4 du noeud 4-GPU V100 ici)
    ##SBATCH --cpus-per-task=3           # nombre de CPU par tache pour gpu_p2 (1/8 du noeud 8-GPU V100)
    ##SBATCH --cpus-per-task=8           # nombre de CPU par tache pour gpu_p5 (1/8 du noeud 8-GPU A100)
    ##SBATCH --cpus-per-task=24          # nombre de CPU par tache pour gpu_p6 (1/4 du noeud 4-GPU H100)
    # /!\ Attention, "multithread" fait reference a l'hyperthreading dans la terminologie Slurm
    #SBATCH --hint=nomultithread         # hyperthreading desactive
    #SBATCH --time=00:10:00              # temps d’execution maximum demande (HH:MM:SS)
    #SBATCH --output=gpu_multi_mpi%j.out # nom du fichier de sortie
    #SBATCH --error=gpu_multi_mpi%j.out  # nom du fichier d'erreur (ici commun avec la sortie)
     
    # Nettoyage des modules charges en interactif et herites par defaut
    module purge
     
    # Decommenter la commande module suivante si vous utilisez la partition "gpu_p5"
    # pour avoir acces aux modules compatibles avec cette partition
    #module load arch/a100
    # Decommenter la commande module suivante si vous utilisez la partition "gpu_p6"
    # pour avoir acces aux modules compatibles avec cette partition
    #module load arch/h100
     
    # Chargement des modules
    module load ...
     
    # Echo des commandes lancees
    set -x
     
    # Pour les partitions "gpu_p5" et "gpu_p6", le code doit etre compile avec les modules compatibles
    # avec la partition choisie
    # Execution du code avec binding via bind_gpu.sh : 1 GPU par tache
    srun /gpfslocalsup/pub/idrtools/bind_gpu.sh ./executable_multi_gpu_mpi

    Pour lancer un script Python, il faut remplacer la dernière ligne par :

    # Execution du code avec binding via bind_gpu.sh : 1 GPU par tache
    srun /gpfslocalsup/pub/idrtools/bind_gpu.sh python -u script_multi_gpu_mpi.py

    Remarque : l'option -u (pour unbuffered) de python permet de désactiver la bufférisation des sorties standards qui est automatiquement effectuée par Slurm.

  • Pour une exécution utilisant 8 GPU, soit 2 nœuds complets, de la partition gpu par défaut :

    multi_gpu_mpi.slurm
    #!/bin/bash
    #SBATCH --job-name=gpu_multi_mpi     # nom du job
    # Il est possible d'utiliser une autre partition que celle par défaut
    # en activant l'une des 5 directives suivantes :
    ##SBATCH -C v100-16g                 # decommenter pour reserver uniquement des GPU V100 16 Go
    ##SBATCH -C v100-32g                 # decommenter pour reserver uniquement des GPU V100 32 Go
    ##SBATCH --partition=gpu_p2          # decommenter pour la partition gpu_p2 (GPU V100 32 Go)
    ##SBATCH -C a100                     # decommenter pour la partition gpu_p5 (GPU A100 80 Go)
    ##SBATCH -C h100                     # decommenter pour la partition gpu_p6 (GPU H100 80 Go)
    # Ici, reservation de 8x10=80 CPU (4 taches par noeud) et de 8 GPU (4 GPU V100 par noeud) sur 2 noeuds :
    #SBATCH --ntasks=8                   # nombre total de tache MPI (= nombre total de GPU)
    #SBATCH --ntasks-per-node=4          # nombre de tache MPI par noeud (= nombre de GPU par noeud)
    #SBATCH --gres=gpu:4                 # nombre de GPU par nœud (max 8 avec gpu_p2, gpu_p5)
    # Le nombre de CPU par tache doit etre adapte en fonction de la partition utilisee. Sachant
    # qu'ici on ne reserve qu'un seul GPU par tache (soit 1/4 ou 1/8 des GPU du noeud suivant
    # la partition), l'ideal est de reserver 1/4 ou 1/8 des CPU du noeud pour chaque tache:
    #SBATCH --cpus-per-task=10           # nombre de CPU par tache (un quart du noeud 4-GPU V100 ici)
    ##SBATCH --cpus-per-task=3           # nombre de CPU par tache pour gpu_p2 (1/8 du noeud 8-GPU V100)
    ##SBATCH --cpus-per-task=8           # nombre de CPU par tache pour gpu_p5 (1/8 du noeud 8-GPU A100)
    ##SBATCH --cpus-per-task=24          # nombre de CPU par tache pour gpu_p6 (1/4 du noeud 4-GPU H100)
    # /!\ Attention, "multithread" fait reference a l'hyperthreading dans la terminologie Slurm
    #SBATCH --hint=nomultithread         # hyperthreading desactive
    #SBATCH --time=00:10:00              # temps d'execution maximum demande (HH:MM:SS)
    #SBATCH --output=gpu_multi_mpi%j.out # nom du fichier de sortie
    #SBATCH --error=gpu_multi_mpi%j.out  # nom du fichier d'erreur (ici commun avec la sortie)
     
    # Nettoyage des modules charges en interactif et herites par defaut
    module purge
     
    # Decommenter la commande module suivante si vous utilisez la partition "gpu_p5"
    # pour avoir acces aux modules compatibles avec cette partition
    #module load arch/a100
    # Decommenter la commande module suivante si vous utilisez la partition "gpu_p6"
    # pour avoir acces aux modules compatibles avec cette partition
    #module load arch/h100
     
    # Chargement des modules
    module load ...
     
    # Echo des commandes lancees
    set -x
     
    # Pour les partitions "gpu_p5" et "gpu_p6", le code doit etre compile avec les modules compatibles
    # avec la partition choisie
    # Execution du code avec binding via bind_gpu.sh : 1 GPU par tache.
    srun /gpfslocalsup/pub/idrtools/bind_gpu.sh ./executable_multi_gpu_mpi

    Pour lancer un script Python, il faut remplacer la dernière ligne par :

    # Execution du code avec binding via bind_gpu.sh : 1 GPU pour 1 tache MPI
    srun /gpfslocalsup/pub/idrtools/bind_gpu.sh python -u script_multi_gpu_mpi.py

    Remarque : l'option -u (pour unbuffered) de python permet de désactiver la bufférisation des sorties standards qui est automatiquement effectuée par Slurm.

Puis soumettre le script via la commande sbatch :

$ sbatch multi_gpu_mpi.slurm

Remarques :

  • Les exécutions sur la partition gpu_p2 se font de manière analogue, en spécifiant --partition=gpu_p2 et --cpus-per-task=3.
  • Les exécutions sur la partition gpu_p5 se font de manière analogue, en spécifiant -C a100 et --cpus-per-task=8. Attention : les modules accessibles par défaut ne sont pas compatibles avec cette partition, il est nécessaire de charger préalablement le module arch/a100 pour pouvoir lister et charger les modules compatibles. Pour en savoir plus, consultez la section Modules compatibles avec la partition gpu_p5.
  • Les exécutions sur la partition gpu_p6 se font de manière analogue, en spécifiant -C h100 et --cpus-per-task=24. Attention : les modules accessibles par défaut ne sont pas compatibles avec cette partition, il est nécessaire de charger préalablement le module arch/h100 pour pouvoir lister et charger les modules compatibles. Pour en savoir plus, consultez la section Modules compatibles avec la partition gpu_p6.
  • Nous vous recommandons de compiler et d'exécuter votre code dans un même environnement en chargeant les mêmes modules.
  • Dans cet exemple, on suppose que l'exécutable executable_multi_gpu_mpi ou le script script_multi_gpu_mpi.py se situe dans le répertoire de soumission, c'est-à-dire le répertoire dans lequel on entre la commande sbatch.
  • Par défaut, Slurm bufferise les sorties standards d'un script Python, ce qui peut impliquer un délai important entre l'exécution du script et la visualisation de ces sorties dans les logs. Pour désactiver cette bufférisation, il faut ajouter l'option -u (pour unbuffered) à l'appel python. Sachez que positionner la variable d'environnement PYTHONUNBUFFERED à 1 dans votre script de soumission (export PYTHONUNBUFFERED=1) a le même effet. Cette variable est positionnée par défaut dans les environnements virtuels installés par l'assistance sur Jean Zay.
  • Le fichier de sortie du calcul multi_gpu_mpi<numero_job>.out se trouve également dans le répertoire de soumission. Il est créé dès le début de l'exécution du travail; l'éditer ou le modifier pendant le déroulement du travail peut perturber celui-ci.
  • Le module purge est rendu indispensable par le comportement par défaut de Slurm : les modules que vous avez chargés dans votre environnement au moment où vous lancez sbatch sont pris en compte dans le job soumis, rendant l'exécution de votre job dépendant de ce que vous aurez fait avant.
  • Pour éviter les erreurs de distribution automatique de tâches, nous vous recommandons d'utiliser srun pour exécuter votre code au lieu de mpirun, ce qui permet de garantir une distribution conforme aux spécifications de ressources demandées dans votre fichier de soumission.
  • Le script /gpfslocalsup/pub/idrtools/bind_gpu.sh permet d'associer un GPU différent à chaque tâche MPI. Il n'est pas nécessaire de l'utiliser si votre code gère explicitement l'association des tâches MPI aux GPU. Attention, ce script est pour l'instant basique et ne permet de gérer que le cas simple 1 GPU pour 1 tâche MPI. Il ne fonctionne donc que pour un nombre de tâches MPI par nœud inférieur ou égal à 4. Si vous avez des besoins plus complexes, merci de contacter l'assistance de l'IDRIS.
  • Les jobs ont tous des ressources définies dans Slurm par une partition et une “Qualité de Service” QoS (Quality of Service) par défaut. Vous pouvez en modifier les limites en spécifiant une autre partition et/ou une QoS comme indiqué dans notre documentation détaillant les partitions et les Qos.
  • Pour les comptes multi-projets ainsi que ceux ayant des heures CPU et GPU, il est indispensable de spécifier l'attribution d'heures sur laquelle décompter les heures de calcul du job comme indiqué dans notre documentation détaillant la gestion des heures de calcul.
  • Nous vous recommandons fortement de consulter notre documentation détaillant la gestion des heures de calcul sur Jean Zay pour vous assurer que les heures consommées par vos travaux soient décomptées de la bonne attribution.