Contenus
afficher
Description
CUDA (Compute Unified Device Architecture) est la plateforme de calcul parallèle développée par NVIDIA. Elle permet d’exécuter du code sur GPU via une extension du C/C++ et un ensemble d’API bas niveau et haut niveau.
CUDA est utilisé dans :
- le calcul scientifique et HPC
- l’IA et le deep learning
- la simulation numérique
- le traitement d’images et de signaux
- les pipelines de rendu et de vision
Quelques points clés :
- Modèle de programmation basé sur kernels, threads, blocs et grilles
- Gestion fine de la mémoire GPU (global, shared, constant, texture)
- Compilation via nvcc
- Outils de profiling (Nsight Compute, Nsight Systems)
- Bibliothèques optimisées : cuBLAS, cuFFT, cuDNN, Thrust, etc.
Mise en place de l’environnement
ml cuda/13.0
- Version(s) disponible(s) : 12.6, 12.8, 12.9, 13.0 (par défaut)
Tutoriels
Monitoring des GPU
# Sur le nœud GPU
nvidia-smi
Compilation
# Aucune obligation d'être sur un noeud GPU
ml gcc
ml cuda
nvcc -o mon_programme mon_programme.cu
L’extension d’un fichier est .cu
- Compilation CUDA (plus avancée) avec activation des warnings du compilateur hôte (gcc)
# -Xcompiler permet de transmettre des options directement à gcc
# -Wall, -Wextra : warnings côté CPU
nvcc -Xcompiler "-Wall -Wextra" add.cu -o add
Mon premier kernel CUDA
Ce programme illustre un premier exemple de calcul parallèle avec CUDA.
Chaque thread GPU calcule la somme de deux éléments de tableaux, ce qui permet d’exploiter le parallélisme massif du GPU.
- Soit le fichier ajoute.cu suivant :
#include <stdio.h>
#include <cuda_runtime.h>
/*
* Kernel CUDA :
* Chaque thread calcule un élément du vecteur c
*/
__global__ void add(int *a, int *b, int *c, int N) {
// Indice global du thread
int i = blockIdx.x * blockDim.x + threadIdx.x;
// Vérification que l'indice est dans les limites du tableau
if (i < N) {
c[i] = a[i] + b[i];
}
}
int main() {
const int N = 256;
int a[N], b[N], c[N];
int *d_a, *d_b, *d_c;
/* Initialisation des tableaux sur le CPU */
for (int i = 0; i < N; i++) {
a[i] = i;
b[i] = 2 * i;
}
/* Allocation mémoire sur le GPU */
cudaMalloc((void**)&d_a, N * sizeof(int));
cudaMalloc((void**)&d_b, N * sizeof(int));
cudaMalloc((void**)&d_c, N * sizeof(int));
/* Copie des données CPU -> GPU */
cudaMemcpy(d_a, a, N * sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, N * sizeof(int), cudaMemcpyHostToDevice);
/* Configuration du lancement du kernel */
int threadsPerBlock = 256;
int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
/* Lancement du kernel sur le GPU */
add<<<blocksPerGrid, threadsPerBlock>>>(d_a, d_b, d_c, N);
/* Synchronisation pour s'assurer que le kernel est terminé */
cudaDeviceSynchronize();
/* Copie du résultat GPU -> CPU */
cudaMemcpy(c, d_c, N * sizeof(int), cudaMemcpyDeviceToHost);
/* Affichage des premiers résultats */
int n_out = 10;
printf("Affichage des %d premières cases du tableau \n", n_out);
for (int i = 0; i < n_out; i++) {
printf("%d + %d = %d\n", a[i], b[i], c[i]);
}
/* Libération de la mémoire GPU */
cudaFree(d_a);
cudaFree(d_b);
cudaFree(d_c);
return 0;
}
- Script de lancement de job launch.sh
#!/bin/bash
#SBATCH --job-name=test_cuda
#SBATCH --partition=bi-h200
#SBATCH --gres=gpu:1
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --mem-per-cpu=4G
#SBATCH --time=00:10:00
#SBATCH --output=job-%j.out
ml gcc/15.2.0
ml cuda/13.0
nvcc -Xcompiler "-Wall -Wextra" ajoute.cu -o ajoute
./ajoute
- Lancement du job
sbatch launch.sh