Table des matières
Jean Zay : MPI CUDA-aware et GPUDirect
Pour une performance optimale, des bibliothèques OpenMPI CUDA-aware supportant le GPUDirect sont disponibles sur Jean Zay.
Ces bibliothèques MPI permettent d'effectuer des communications utilisant des buffers d'envoi et de réception alloués sur la mémoire du GPU. Grâce au support de GPUDirect, ces transferts se font directement de GPU à GPU sans recopie intermédiaire sur la mémoire du CPU, lorsque c'est possible.
Attention : Le GPUDirect n'est pas utilisable sur Jean Zay pour les codes utilisant plusieurs GPU par processus MPI.
Compilation du code
Il est nécessaire de compiler le code en utilisant l'une des bibliothèques OpenMPI CUDA-aware disponibles sur Jean Zay.
Après avoir chargé le compilateur que vous souhaitez utiliser, vous devez charger l'un des modules suivants :
$ module avail openmpi/*-cuda ------------- /gpfslocalsup/pub/modules-idris-env4/modulefiles/linux-rhel8-skylake_avx512 ------------- openmpi/3.1.4-cuda openmpi/3.1.6-cuda openmpi/4.0.2-cuda openmpi/4.0.4-cuda $ module load openmpi/4.0.4-cuda
Si OpenMPI n'est pas disponible pour le compilateur désiré, un message d'erreur sera affiché. N'hésitez pas à contacter l'assistance pour demander une nouvelle installation si nécessaire.
Pour connaître la liste des compilateurs pour lesquels une version donnée d'OpenMPI est disponible, vous pouvez utiliser la commande module show openmpi/<version>
. Par exemple :
$ module show openmpi/4.0.2-cuda ------------------------------------------------------------------ /gpfslocalsup/pub/modules-idris-env4/modulefiles/linux-rhel8-skylake_avx512/openmpi/4.0.2-cuda: module-whatis {An open source Message Passing Interface implementation.} prereq intel-compilers/19.0.4 pgi/20.1 pgi/19.10 gcc/10.1.0 gcc/8.3.1 conflict openmpi conflict intel-mpi Available software environment(s): - intel-compilers/19.0.4 - pgi/20.1 - pgi/19.10 - gcc/10.1.0 - gcc/8.3.1 If you want to use this module with another software environment, please contact the support team. -------------------------------------------------------------------
La compilation se fait en utilisant les wrappers d'OpenMPI :
$ mpifort source.f90 $ mpicc source.c $ mpic++ source.C
Aucune option particulière n'est nécessaire pour la compilation, vous pouvez vous référer à la rubrique Compilation GPU de l'index pour plus d'information sur la compilation des codes utilisant les GPU.
Adaptation du code
L'utilisation de la fonctionnalité MPI CUDA-aware GPUDirect sur Jean Zay impose de respecter un ordre d'initialisation bien précis pour CUDA ou OpenACC et MPI dans le code :
- initialisation de CUDA ou OpenACC
- choix du GPU que chaque processus MPI doit utiliser (étape de binding)
- initialisation de MPI.
Attention : si cet ordre d'initialisation n'est pas respecté, l'exécution de votre code risque de planter avec l'erreur suivante :
CUDA failure: cuCtxGetDevice()
Une légère adaptation de votre code peut donc être nécessaire pour pouvoir profiter de cette fonctionnalité sur Jean Zay.
Vous trouverez ci-dessous un exemple de sous-routine en Fortran et en C permettant d'initialiser OpenACC avant d'initialiser MPI ainsi qu'un exemple CUDA.
Attention : cet exemple ne fonctionne que dans le cas où on alloue exactement un processus MPI par GPU.
Exemple OpenACC
Version Fortran
- init_acc.f90
#ifdef _OPENACC subroutine initialisation_openacc USE openacc character(len=6) :: local_rank_env integer :: local_rank_env_status, local_rank ! Initialisation d'OpenACC !$acc init ! Récupération du rang local du processus via la variable d'environnement ! positionnée par Slurm, l'utilisation de MPI_Comm_rank n'étant pas encore ! possible puisque cette routine est utilisée AVANT l'initialisation de MPI call get_environment_variable(name="SLURM_LOCALID", value=local_rank_env, status=local_rank_env_status) if (local_rank_env_status == 0) then read(local_rank_env, *) local_rank ! Définition du GPU à utiliser via OpenACC call acc_set_device_num(local_rank, acc_get_device_type()) else print *, "Erreur : impossible de déterminer le rang local du processus" stop 1 end if end subroutine initialisation_openacc #endif
Exemple d'utilisation :
- init_acc_mpi.f90
! On initialise OpenACC... #ifdef _OPENACC call initialisation_openacc #endif ! ... avant d'initialiser MPI call mpi_init(code)
Version C
- init_acc.c
#ifdef _OPENACC void initialisation_openacc() { char* local_rank_env; int local_rank; /* Initialisation d'OpenACC */ #pragma acc init /* Récupération du rang local du processus via la variable d'environnement positionnée par Slurm, l'utilisation de MPI_Comm_rank n'étant pas encore possible puisque cette routine est utilisée AVANT l'initialisation de MPI */ local_rank_env = getenv("SLURM_LOCALID"); if (local_rank_env) { local_rank = atoi(local_rank_env); /* Définition du GPU à utiliser via OpenACC */ acc_set_device_num(local_rank, acc_get_device_type()); } else { printf("Erreur : impossible de déterminer le rang local du processus\n"); exit(1); } } #endif
Exemple d'utilisation :
- init_acc_mpi.c
#ifdef _OPENACC /* On initialise OpenACC... */ initialisation_openacc(); #endif /* ... avant d'initialiser MPI */ MPI_Init(&argc, &argv);
Exemple CUDA
- init_cuda.c
#include <cuda.h> #include <cuda_runtime.h> void initialisation_cuda() { char* local_rank_env; int local_rank; cudaError_t cudaRet; /* Récupération du rang local du processus via la variable d'environnement positionnée par Slurm, l'utilisation de MPI_Comm_rank n'étant pas encore possible puisque cette routine est utilisée AVANT l'initialisation de MPI */ local_rank_env = getenv("SLURM_LOCALID"); if (local_rank_env) { local_rank = atoi(local_rank_env); /* Définition du GPU à utiliser pour chaque processus MPI */ cudaRet = cudaSetDevice(local_rank); if(cudaRet != CUDA_SUCCESS) { printf("Erreur: cudaSetDevice a échoué\n"); exit(1); } } else { printf("Erreur : impossible de déterminer le rang local du processus\n"); exit(1); } }
Exemple d'utilisation :
- init_cuda_mpi.c
/* On initialise CUDA... */ initialisation_cuda(); /* ... avant d'initialiser MPI */ MPI_Init(&argc, &argv);
Exécution du code
Lors de l'exécution, vous devez vous assurez de charger avec la commande module
la même bibliothèque MPI que celle qui a été utilisée pour la compilation du code, puis de bien utiliser la commande srun
pour démarrer celui-ci.
Le support de CUDA-aware et GPUDirect est alors activé par défaut sans opération supplémentaire.
Pour plus d'informations sur la soumission des travaux multi-GPU utilisant MPI, consultez la page exécution d'un travail multi-GPU MPI CUDA-aware et GPUDirect en batch.