Contrôle de l'ordonnancement avec MPI

From CC Doc
Jump to: navigation, search
This page is a translated version of the page Advanced MPI scheduling and the translation is 100% complete.

Other languages:
English • ‎français

La plupart des utilisateurs devraient soumettre les tâches MPIMessage Passing Interface parallèles à mémoire distribuée selon l'exemple présenté à section Tâche MPIMessage Passing Interface de la page Exécuter des tâches. Il suffit d'utiliser - ntasks ou -n pour spécifier le nombre de processus et de laisser l'ordonnanceur faire la meilleure allocation, compte tenu de l'efficacité de la grappe.

Si par contre vous voulez plus de contrôle sur l'allocation, prenez connaissance de la page Support for Multi-core/Multi-thread Architectures de SchedMD; on y décrit comment plusieurs options de la commande sbatch agissent sur l'ordonnancement des processus.

Dans la foire aux questions Slurm, la réponse à What exactly is considered a CPU? peut aussi s'avérer utile.

Exemples de scénarios

Peu de cœurs, nœuds indéterminés

En plus de spécifier la durée de toute tâche Slurm, il faut aussi indiquer le nombre de processus MPIMessage Passing Interface que Slurm doit démarrer. Le moyen le plus simple de ce faire est d'utiliser --ntasks. Puisque l'allocation par défaut de 256Mo de mémoire est souvent insuffisante, vous devriez aussi spécifier la quantité de mémoire nécessaire. Avec --ntasks, il est impossible de savoir combien de cœurs seront sur chaque nœud; vous voudrez alors utiliser --mem-per-cpu ainsi

--ntasks=15
--mem-per-cpu=3G
srun application.exe

Nous avons ici 15 processus MPIMessage Passing Interface. L'allocation des cœurs pourrait se faire sur 1 nœud, sur 15 nœuds, ou sur tout nombre de nœuds entre 1 et 15.

Nœuds entiers

Pour une tâche parallèle intensive qui peut utiliser efficacement 32 cœurs ou plus, vous devriez probablement demander des nœuds entiers; il est donc utile de savoir quels types de nœuds sont disponibles sur la grappe que vous utilisez.

La plupart des nœuds de Cedar, Graham et Niagara sont configurés comme suit ː

grappe cœurs mémoire utilisable notes
Graham 32 125 Gio (~3.9 Gio/cœur) Certains sont réservés pour les tâches sur nœuds entiers.
Cedar (Broadwell) 32 125 Gio (~3.9 Gio/cœur)
Cedar (Skylake) 48 187 Gio (~3.9 Gio/cœur) Certains sont réservés pour les tâches sur nœuds entiers.
Niagara 40 188 Gio Cette grappe n'accepte que les tâches sur nœuds entiers.

Les tâches sur nœuds entiers peuvent être exécutées sur tous les nœuds. Dans le tableau ci-dessus, la note « Certains sont réservés pour les tâches sur nœuds entiers » signifie que les tâches par cœur sont interdites sur certains nœuds.

Voici un exemple d'un script demandant des nœuds entiers.

File : whole_node_graham.sh

#!/bin/bash 
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=32
#SBATCH --mem=0
srun application.exe
File : whole_node_cedar.sh

#!/bin/bash 
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=48
#SBATCH --mem=0
srun application.exe
File : whole_node_niagara.sh

#!/bin/bash 
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=40  # or 80: Hyperthreading is enabled
#SBATCH --mem=0
srun application.exe

Le fait de demander --mem=0 indique à Slurm qu'il doit réserver toute la mémoire disponible de chacun des nœuds assignés à la tâche.

Toutefois, si vous avez besoin de plus de mémoire par nœud que ce que le plus petit nœud peut offrir (par exemple plus de 125Gio sur Graham), vous ne devriez pas utiliser --mem=0, mais demander une quantité explicite de mémoire. De plus, une partie de la mémoire de chaque nœud est réservée au système d'exploitation, alors la plus grande quantité de mémoire qu'une tâche peut demander pour chacun des types de nœuds est comme suit ː

taille du nœud requête à Slurm grappe
128 Go = 125 Gio --mem=128000M Graham, Cedar Broadwell
192 Go = 187 Gio --mem=192000M Cedar Skylake
256 Go = --mem=256000M Graham, Cedar Broadwell
512 Go = --mem=512000M Graham, Cedar Broadwell
1.5 To = --mem=1500G Cedar Broadwell
3 To = --mem=3000G Graham, Cedar Broadwell

Le fait de demander --mem=256G plutôt que --mem=256000M signifie que la tâche n'attendra pas un nœud de 256Go. Elle ne sera pas rejetée par Slurm, mais restera en attente plus longtemps que nécessaire pour un nœud plus rare de plus grande capacité.

Peu de cœurs, nœud unique

Si vous avez besoin de moins qu'un nœud entier, mais que tous les cœurs doivent être du même nœud, vous pouvez demander par exemple

--nodes=1
--ntasks-per-node=15
--mem=45G
srun application.exe

Tâche intensive en parallèle, sans multiples de nœuds entiers

Ce ne sont pas toutes les tâches qui sont effectuées de façon optimale sur des cœurs en multiples de 32, 40 ou 48. Le fait d'indiquer ou non un nombre précis de cœurs peut influer sur le temps d'exécution (ou la bonne utilisation de la ressource) ou le temps d'attente (ou la bonne utilisation du temps qui vous est imparti). Pour de l'aide sur comment évaluer ces facteurs, communiquez avec le soutien technique.

Tâches hybrides : MPIMessage Passing Interface avec OpenMP ou MPIMessage Passing Interface avec fils

Il est important de bien comprendre que le nombre de tâches Slurm demandées représente le nombre de processus qui seront démarrés avec srun. Dans le cas d'une tâche hybride qui utilise à la fois des processus MPIMessage Passing Interface et des fils OpenMP ou Posix, vous voudrez indiquer le nombre de processus MPIMessage Passing Interface avec --ntasks ou -ntasks-per-node et ;le nombre de fils avec --cpus-per-task.

--ntasks=16
--cpus-per-task=4
--mem-per-cpu=3G
srun application.exe

Ici, 64 cœurs sont alloués, mais seulement 16 processus MPIMessage Passing Interface sont et seront initialisés. S'il s'agit en plus d'une application OpenMP, chacun des processus démarrera 4 fils, soit un par cœur. Chaque processus pourra utiliser 12Go. Avec 4 cœurs, les tâches pourraient être allouées sur 2 à 16 nœuds, peu importe lesquels.

--nodes=2
--ntasks-per-node=8
--cpus-per-task=4
--mem=96G
srun application.exe

La taille de cette tâche est identique à celle de la précédente, c'est-à-dire 16 tâches (soit 16 processus MPIMessage Passing Interface) avec chacune 4 fils. La différence ici est que nous obtiendrons exactement 2 nœuds entiers. N'oubliez pas que --mem précise la quantité de mémoire par nœud et que nous l'utilisons de préférence à --mem-per-cpu pour la raison donnée plus haut.

Pourquoi srun plutôt que mpiexec ou mpirun?

mpirun permet la communication entre processus exécutés sur des ordinateurs différents; les ordonnanceurs récents possèdent cette même fonctionnalité. Avec Torque/Moab, il n'est pas nécessaire de fournir à mpirun la liste des nœuds ou le nombre de processus puisque l'ordonnanceur s'en charge. Avec Slurm, c'est l'ordonnanceur qui décide de l'affinité des tâches, ce qui évite d'avoir à préciser des paramètres comme

mpirun --map-by node:pe=4 -n 16  application.exe

Dans les exemples précédents, on comprend que srun application.exe distribue automatiquement les processus aux ressources précises allouées à la tâche.

En termes de niveaux d'abstraction, srun est au-dessus de mpirun; srun peut même faire plus que mpirun. Avec Slurm, srun prend en charge tous les types de calculs; il est aussi plus polyvalent que le pbsdsh de Torque. On pourrait dire de srun que c'est l'outil Slurm universel pour la distribution des tâches en parallèle  une fois les ressources allouées, la nature de l'application importe peu, qu'il s'agisse de MPIMessage Passing Interface, OpenMP, hybride, distribution sérielle, pipeline, multiprogramme ou autre.

Bien entendu, srun est parfaitement adapté à Slurm : il amorce la première étape de la tâche, initialise correctement les variables d'environnement SLURM_STEP_ID et SLURM_PROCID et fournit les renseignements de suivi appropriés.

Pour des exemples de quelques différences entre srun et mpiexec, voyez le forum OpenMPI. Dans certains cas, mpiexec offrira une meilleure performance que srun, mais srun diminue le risque de conflit entre les ressources allouées par Slurm et celles utilisées par OpenMPI.

Références

Categorie:SLURM