Nvidia CUDA

NVIDIA CUDA (Compute Unified Device Architecture) est une plateforme de calcul parallèle et un modèle de programmation développé par NVIDIA. CUDA permet aux développeurs d'utiliser des GPU NVIDIA haute performance (Graphics Processing Units) pour effectuer des tâches de calcul à usage général. Grâce à CUDA, les développeurs peuvent exploiter les puissantes capacités de calcul des GPU pour des calculs parallèles à grande échelle, améliorant considérablement les performances des applications, en particulier dans des domaines tels que le calcul scientifique, l'apprentissage automatique, le traitement d'images et la simulation physique. CUDA prend en charge plusieurs langages de programmation, tels que C, C++ et Python, et fournit un ensemble riche de bibliothèques et d'outils pour aider les développeurs à obtenir facilement le calcul accéléré par GPU.

cuda logo

Bases de la programmation CUDA

Comment pouvons-nous invoquer les ressources GPU dans un programme pour accélérer l'exécution du code? En utilisant CUDA, nous pouvons facilement gérer des milliers de cœurs de calcul au sein du GPU. Le modèle de programmation CUDA est un modèle hétérogène qui nécessite que le CPU et le GPU travaillent ensemble. Dans la programmation CUDA, les termes "host" et "device" sont utilisés pour distinguer les appareils où le code est exécuté.

  • Host: CPU et mémoire hôte
  • Device: GPU et mémoire vidéo

Généralement, le programme principal se charge sur le CPU, puis copie les données d'initialisation vers le GPU. Le GPU effectue les calculs et copie les résultats vers le CPU.

Nous pouvons utiliser un kernel dans un programme CUDA pour implémenter le processus ci-dessus. Dans CUDA, un kernel fait référence à une fonction qui, lorsqu'elle est appelée, provoque le lancement par le GPU de nombreux threads simultanément pour exécuter ce kernel, réalisant ainsi le parallélisme. Chaque thread exécute le kernel en utilisant son ID de thread pour correspondre à l'index des données d'entrée, garantissant que chaque thread exécute le même kernel mais traite des données différentes.

Le GPU a un grand nombre de cœurs de calcul, et CUDA utilise une structure organisationnelle à deux niveaux pour gérer ces cœurs. Voici trois concepts de programmation CUDA qui doivent être introduits: Thread, Block et Grid.

  • Thread: Un programme parallèle CUDA est exécuté par de nombreux threads.
  • Block: Plusieurs threads sont regroupés pour former un bloc.
  • Grid: Plusieurs blocs sont ensuite organisés en une grille.

Note: Les threads au sein du même bloc peuvent se synchroniser et communiquer via la mémoire partagée.

cpu and gpu

Dans CUDA, chaque thread a un identifiant unique appelé ThreadIdx, qui change en fonction de la façon dont le Grid et le Block sont divisés.

De plus, voici une brève introduction au modèle de mémoire CUDA, comme illustré dans le diagramme ci-dessous. Chaque thread a sa propre mémoire locale privée, tandis que chaque bloc de threads comprend une mémoire partagée accessible par tous les threads du bloc, avec une durée de vie égale à celle du bloc de threads. De plus, tous les threads peuvent accéder à la mémoire globale. Il existe également des espaces mémoire en lecture seule: mémoire constante et mémoire de texture. La structure mémoire implique l'optimisation du programme, qui ne sera pas discutée en détail ici.

cpu and gpu

Cas de démonstration CUDA

Maintenant, découvrons l'accélération GPU à travers un exemple simple. Dans cet exemple, nous calculerons la somme de deux vecteurs sur le CPU et le GPU, et comparerons les vitesses de calcul entre CPU et GPU.

Préparation de l'environnement d'exécution

Le système d'exploitation JetPack inclut déjà l'environnement d'exécution CUDA. Nous pouvons vérifier la version de CUDA en utilisant l'outil jtop. Ouvrez le terminal et exécutez jtop.

bash
jtop

jtop

Note: Si jtop n'est pas installé sur votre système, veuillez consulter ce guide.

Ci-dessous, nous utiliserons Python pour écrire du code CUDA. Nous devons donc exécuter la commande suivante dans le terminal pour installer Numba.

bash
pip install numba

Exécution du code d'exemple

Étape 1: Créez un script Python pour ce cours dans le répertoire racine:

bash
mkdir -p ~/reComputer_J/3.4_CUDA
touch ~/reComputer_J/3.4_CUDA/add_compare.py

Étape 2: Ouvrez VSCode dans le répertoire où se trouve le script:

bash
cd ~/reComputer_J/3.4_CUDA
code .

Étape 3: Dans la barre de navigation de gauche, nous pouvons voir hello_world.py. Entrez le code suivant dans le fichier python:

Cliquez pour développer le code
python
from numba import cuda, float32
import numpy as np
import time


@cuda.jit
def add_gpu(a, b, out):
  tx = cuda.threadIdx.x
  ty = cuda.blockIdx.x
  block_size = cuda.blockDim.x
  grid_size = cuda.gridDim.x
  start = tx + ty * block_size
  stride = block_size * grid_size

  for i in range(start, a.shape[0], stride):
      out[i] = a[i] + b[i]

def add_cpu(a, b, out):
  for i in range(a.shape[0]):    
      out[i] = a[i] + b[i]

def main():
  # Prepare test data
  n = 10000000
  a = np.arange(n).astype(np.float32)
  b = np.arange(n).astype(np.float32)
  out = np.empty_like(a)

  # 1. Test CPU computation time
  start = time.time()
  add_cpu(a, b, out)
  print("CPU Time Taken:", time.time()-start)

  # 2. Test GPU computation time
  # Copy data from host memory to device memory
  d_a = cuda.to_device(a)
  d_b = cuda.to_device(b)
  d_out = cuda.device_array_like(out)
  # Define the number of threads per block and the number of blocks
  threads_per_block = 256  
  blocks_per_grid = (n + threads_per_block - 1) // threads_per_block
  # Compute the result
  start_gpu = time.time()
  add_gpu[blocks_per_grid, threads_per_block](d_a, d_b, d_out)
  end_gpu = time.time()
  # Copy the result from device memory back to host memory
  d_out.copy_to_host(out)

  print("GPU Time Taken:", end_gpu-start_gpu)


if __name__ == "__main__":
  main()

Étape 4: Cliquez sur le bouton d'exécution pour voir le résultat du script Python dans le terminal.

launch vscode

D'après les résultats imprimés dans le terminal, on peut observer que la vitesse de calcul du GPU est plus rapide.

Plus de tutoriels d'apprentissage CUDA

TutorielTypeDescription
Getting Started with CUDA Programming(zh)DocumentUn tutoriel minimaliste pour commencer avec la programmation CUDA en chinois
CUDA C++ Programming GuideDocumentLe guide de programmation pour le modèle et l'interface CUDA.