Aller au contenu

GPGPU

Accélération d'applications sur processeur graphique (GPU)

Documentation

  • CUDA

    L'API CUDA, qui s'appuie sur du langage C/C++, permet l'accélération de codes de calcul sur des GPU de type NVIDIA : https://developer.nvidia.com/cuda-zone

    Sur le système de fichiers de Myria, les répertoires /soft/cuda_<version>/cuda/doc et /soft/cuda_<version>/cuda/doc/html contiennent les documentations de CUDA 8.0 et 9.1

  • CUDA FORTRAN

    La version FORTRAN de l'API CUDA a été initialement éditée par PGI.

    La documentation utilisateur est disponible au format PDF sur le site de l’éditeur : https://www.pgroup.com/resources/docs/19.7/pdf/pgi19cudaforug.pdf

    La dernière version est éditée par NVIDIA : https://docs.nvidia.com/hpc-sdk/compilers/cuda-fortran-prog-guide/index.html

  • Directives OpenACC

    Les directives OpenACC (http://www.openacc-standard.org/) constituent un standard de la programmation par directives pour GPU. La norme OpenACC 2.6 est supportée par le compilateur PGI 18.4.
    Les développements sont simplifiés par rapport à l'utilisation directe de l'API CUDA. Cette dernière permet parfois d'exploiter plus finement l'architecture d'un GPU. Dans d'autres cas, OpenACC peut être efficace et réduire le rapport temps de développement / performance. OpenACC peut aussi être utile pour aborder un travail de portage : certaines conclusions tirées peuvent être mise en œuvre ultérieurement avec CUDA.

  • Directives OpenMP target

    Les directives OpenMP target sont supportées notamment par le compilateur "NVIDIA HPC SDK", à partir de sa version 20.11.

    Sur Myria, le support de cours /soft/formations/OpenMP-4to5-ATOS-CRIANN-2020/OpenMP4to5-final.pdf comprend une partie finale "Data Management for Devices" portant sur ces directives.

Environnement logiciel

Les commandes suivantes activent la version voulue de CUDA, du compilateur PGI (pour OpenACC ou CUDA FORTRAN) ou du compilateur NVIDIA (HPC SDK, pour OpenACC, CUDA FORTRAN ou OpenMP target) sur Myria. Pour ces outils, des versions plus récentes que celles mentionnées plus bas peuvent être disponibles (voir module avail).

Ces commandes doivent être exécutées sur l'une des frontales pour les compilations, et dans les commandes d'un script de soumission sur ressource GPU.

  • CUDA

    login@Myria-1:~ module load cuda/9.1
    
    cuda/9.1 environment
    
    • Modèles de Makefile : /soft/makefiles/SERIAL_GPU_CODES (exemples CUDA et OpenCL), /soft/makefiles/MPI_CUDA_CODES/MakeIntelMPI_CUDA_C++
  • CUDA avec librairie MPI (Open MPI 3.0.1)

    login@Myria-1:~ module purge
    login@Myria-1:~ module load cuda/9.1 mpi/openmpi-3.0.1_cuda-9.1
    
    load cuda 9.1 environment
    load compilers/gnu/5.5.0 environment
    load openmpi-3.0.1_cuda-9.1 environment
    
    • Modèle de Makefile pour code CUDA + Open MPI 3.0.1 : /soft/makefiles/MPI_CUDA_CODES/MakeOpenMPI_CUDA_C++
  • OpenACC ou CUDA FORTRAN

    login@Myria-1:~ module load pgi/18.4
    
    pgi/18.4 environment set (OpenACC and CUDA FORTRAN supports)
    
    • Modèle de Makefile pour CUDA FORTRAN : /soft/makefiles/MPI_CUDA_CODES/MakeIntelMPI_CUDA_Fortran
    • Modèle de Makefile pour OpenACC : /soft/makefiles/MPI_OpenACC_CODES/MakeIntelMPI_PGI_OpenACC
  • OpenACC avec librairie MPI (Open MPI 3.0.1)

    login@Myria-1:~ module purge
    login@Myria-1:~ module load pgi/18.4 mpi/pgi-18.4_openmpi-3.0.1
    
    load pgi/18.4 environment (OpenACC and CUDA FORTRAN support)
    load pgi-18.4_openmpi-3.0.1 environment
    
    • Modèle de Makefile pour code OpenACC + Open MPI 3.0.1 : /soft/makefiles/MPI_OpenACC_CODES/MakeOpenMPI_PGI_OpenACC
  • OpenACC, CUDA FORTRAN ou OpenMP target avec librairie MPI (Open MPI 4.0.5)

    login@Myria-1:~ module purge
    login@Myria-1:~ module load -s mpi/nvhpc-21.7_openmpi-4.0.5-cuda-11.1
    login@Myria-1:~ module list
    Currently Loaded Modulefiles:
    1) nvidia-compilers/21.7   2) cuda/11.1   3) mpi/nvhpc-21.7_openmpi-4.0.5-cuda-11.1
    
    • Modèle de Makefile pour code CUDA FORTRAN (options du compilateur NVIDIA) + Open MPI 4.0.5 : /soft/sample_codes/cuda_fortran/P2DJ_CUF/P2DJ_CUF_Directive_Kernel/Makefile_CudaFortran
    • Modèle de Makefile pour code OpenMP target + Open MPI 4.0.5 : /soft/sample_codes/openmp_target/P2DJ_OpenMP-target/Makefile

Soumission des calculs

Les nœuds de calcul GPU de Myria acceptent les partitions (classes de soumission) gpu_all, gpu_court, gpu_k80, gpu_p100 et gpu_v100.

Un ou plusieurs nœuds de calcul GPU peuvent être dédiés ponctuellement à un utilisateur, sur demande, à des fins de travaux de développement (nécessitant une haute disponibilité de la ressource GPU) : Contacter support@criann.fr.

Pour exécuter un code accéléré sur GPU, il suffit d'ajouter les directives :

# Partition (gpu_k80, gpu_p100 or gpu_v100)
#SBATCH --partition gpu_p100
# GPUs per compute node
#SBATCH --gres gpu:2

et l'activation des environnements :

# Source cuda or pgi environment
module load cuda/10.0
#module load pgi/19.10

dans l'un des modèles de script job_serial.sl, job_OpenMP.sl, job_MPI(_OpenMP).sl présents dans /soft/slurm/criann_modeles_scripts. Le fichier /soft/slurm/criann_modeles_scripts/job_MPI_OpenMP_GPU.sl fournit l'exemple pour un code MPI / OpenMP / CUDA.

Remarque : dans le cas d’un code MPI accéléré sur GPU, l’application des directives #SBATCH --nodes et #SBATCH --ntasks-per-node (à la place de #SBATCH --ntasks) est utile.

En effet, s’il est souhaité que chaque processus MPI de l’application adresse un GPU différent, il suffit d’appliquer #SBATCH --ntasks-per-node 4 en partition gpu_k80 (car les serveurs visés ont 4 GPUs (2 cartes Kepler K80)) ou #SBATCH --ntasks-per-node 2 en partition gpu_p100 (car les serveurs visés ont 2 GPUs (2 cartes Pascal P100)).

Exemple :

##
# Partition (gpu_k80, gpu_p100 or gpu_v100)
#SBATCH --partition gpu_p100
#
# GPUs per compute node
#SBATCH --gres gpu:2
#
# Compute nodes number
#SBATCH --nodes 3
#
# MPI tasks per compute node
#SBATCH --ntasks-per-node 2
#
# Threads per MPI tasks (if needed)
#SBATCH --cpus-per-task 8

Architecture V100-SXM2

Cinq serveurs sont dotés chacun de quatre GPU V100-SXM2 (32 GB de mémoire), deux CPU SkyLake (x 16 cœurs à 2,1 GHz) et 187 GB de RAM DDR4.

Les quatre GPU d'un nœud de calcul sont interconnectés deux à deux par un lien NVLink2 à 100 GB/s de bande passante (2 x 50 GB/s dans chaque direction).

Ces machines possèdent chacune deux interfaces réseau Omni-Path et sont accessibles par la partition gpu_v100 avec Slurm.

CUDA aware MPI

La configuration permet l'exécution de codes programmés par approche CUDA aware MPI (appel de fonctions MPI pour des données allouées en mémoire GPU).

Ce type de code peut exploiter le NVLink pour les échanges MPI intra-nœud.

Les échanges (cuda aware) MPI inter-nœud bénéficient eux aussi d'un accès direct de mémoire GPU à mémoire GPU, par le réseau rapide (technologie GPUDirect RDMA supportée par Omni-Path).

Environnements

  • Open MPI 4.0.5, CUDA 11.1 et gcc 7.3.0

    login@Myria-1:~ module load mpi/openmpi-4.0.5-cuda-11.1
    
  • ou : Open MPI 4.0.2, CUDA 10.1 et icc 17.1

    login@Myria-1:~ module load mpi/openmpi-4.0.2-icc-17.1-cuda-10.1
    
  • ou : Open MPI 4.0.5, CUDA 11.1 et PGI 20.7 (pour OpenACC (C, C++, FORTRAN) ou CUDA FORTRAN)

    login@Myria-1:~ module load mpi/pgi-20.7_openmpi-4.0.5-cuda-11.1
    
  • ou : Open MPI 4.0.5, CUDA 11.1 et nvhpc 21.7 (pour OpenACC (C, C++, FORTRAN), CUDA FORTRAN ou les directives Open MP target pour GPU)

    login@Myria-1:~ module load mpi/nvhpc-21.7_openmpi-4.0.5-cuda-11.1
    

Programmation

Dans une approche CUDA+MPI, une tâche MPI doit être affectée à un GPU par des fonctions appropriées : cudaSetDevice() pour CUDA ou acc_set_device_num() avec OpenACC.

Pour CUDA aware MPI avec Omni-Path (réseau d'interconnexion de Myria), cette association processus parallèle / GPU doit être effectuée avant l'appel à MPI_Init().

Les éléments de programmation pour effectuer cette association sont fournis par l'IDRIS (pour le cas de Slurm, batch de Myria également), pour CUDA (C) ou OpenACC :

http://www.idris.fr/eng/jean-zay/gpu/jean-zay-gpu-mpi-cuda-aware-gpudirect-eng.html

Dans le cas d'utilisation de CUDA FORTRAN, cette association doit se faire par le code suivant :

    subroutine init_device()
        USE cudafor

        implicit  none

        character(len=6) :: local_rank_env
        integer          :: local_rank_env_status, local_rank, istat

        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
            istat = cudaSetDevice(local_rank)
        else
            print *, "Slurm batch system must be used"
            STOP 1
        end if
      end subroutine init_device

Dans le cas d'utilisation des directives OpenMP target, avec le compilateur nvidia (module mpi/nvhpc indiqué ci-dessus), cette association doit se faire par le code suivant :

    subroutine init_device()
        USE cudafor
        USE omp_lib

        implicit  none

        character(len=6) :: local_rank_env
        integer          :: local_rank_env_status, local_rank, istat

        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
            istat = cudaSetDevice(local_rank)
            call omp_set_default_device(local_rank)
        else
            print *, "Slurm batch system must be used"
            STOP 1
        end if
      end subroutine init_device

Exemples didactiques de programmes

CUDA FORTRAN

/soft/sample_codes/cuda_fortran/P2DJ_CUF/ sur Myria

Cet exemple de code utilise l'API CUDA FORTRAN. Il alloue certaines données en mémoire hôte non paginée (mot clé PINNED dans l'instruction FORTRAN déclarant le tableau). Ces données sont alors transférées de manière optimisée pour la performance, entre le GPU et l'hôte par la fonction cudaMemcpyAsync(). Les allocations de données en mémoire GPU se font par le mot clé DEVICE dans l'instruction FORTRAN déclarant le tableau. Ce programme appelle MPI de hôte à hôte. Le code est décliné en deux variantes :

  • Rédaction des kernels CUDA sous forme classique (analogue à CUDA C) : /soft/sample_codes/cuda_fortran/P2DJ_CUF/P2DJ_CUF_Classical_Kernel/
  • Rédaction des kernels CUDA à l'aide de la directive !$cuf kernel do de NVIDIA : /soft/sample_codes/cuda_fortran/P2DJ_CUF/P2DJ_CUF_Directive_Kernel/

    Cette approche combine la convivialité des directives pour les kernels, et les fonctions CUDA pour les transferts optimisés de données entre hôte et GPU. L'exemple emploie la forme asynchrone de la directive !$cuf kernel do (qui met en jeu la notion de stream de CUDA).

Directives OpenMP target

/soft/sample_codes/openmp_target/P2DJ_OpenMP-target sur Myria

Cet exemple de code utilise les directives OpenMP target en FORTRAN (!$omp target).

Il effectue des communications MPI de GPU à GPU (cuda aware MPI).

L'association entre tâche MPI et GPU est faite avant l'appel à MPI_Init(), par le sous-programme init_device() (également rédigé ci-dessus).


Dernière mise à jour: 1 février 2024 10:18:06