• Aucun résultat trouvé

3.4 Émergence des GPUs comme un outil de calcul général

3.4.5 Architecture matérielle et logiciel du GPU

Conduit par la demande pour des graphiques 3D à haute définition sur des ordinateurs personnels, les GPUs ont évolué en un environnement fortement parallèle, multithreads et multicoeurs [132]. Dans la suite du document, une vue générale de l’architecture matérielle et logicielle sera présentée.

3.4.5.1 Vue générale de l’architecture matérielle

Le processeur GPU (Graphics Processing Unit) orienté débit représente une tendance majeure dans l’avancement récent des architectures pour l’accélération du calcul paral- lèle. Effectivement, cette architecture offre une puissance de calcul énorme et une bande passante de mémoire très élevée par rapport aux CPUs traditionnels. Puisque plus de transistors sont dévoués au traitement des données plutôt qu’aux caches de données et aux flots de contrôle, le GPU est spécialisé pour des applications fortement parallèles et intenses en termes de calcul. Une revue complète sur l’architecture du GPU peut être consultée dans [140], [102], [188].

La plus évidente caractéristique du GPU est la disponibilité d’un très grand nombre de coeurs assez simples au lieu de quelques coeurs complexes comme les processeurs classiques multicoeurs de bureau à usage général.

Du point de vue physique, les GPUs NVIDIA sont organisés comme des Streaming Multiprocessors (SM : multiprocesseurs de traitement de flux) embarquant des proces- seurs scalaires élémentaires (SP : Scalar Processors). Les SMs utilisent la mémoire du périphérique comme une ressource partagée. Chaque SM contient par ailleurs un nombre important de registres, utilisés pour stocker les opérandes des instructions [140].

Par exemple, pour chaque type de GPU envisagé, il convient donc de tenir compte de ces spécificités en termes de répartition des unités de calcul et de modèle d’exécution, ainsi que de la hiérarchie mémoire.

3.4. ÉMERGENCE DES GPUS COMME UN OUTIL DE CALCUL GÉNÉRAL

Figure 3.7 – Une représentation simplifié de l’architecture GPU de NVIDIA GTX 280 (à gauche) et GTX 480 (à droite).

3.4.5.2 Vue générale de l’architecture logicielle

Comme nous l’avons indiqué dans la partie matérielle, l’architecture du GPU varie considérablement à chaque génération. En outre, même dans une génération, la consi- dération de la puce (en particulier, le nombre de coeurs) peut être modifiée. Ceci peut entrainer des complications lors du portage des programmes à différentes architectures ci- blées. Pour pallier ce problème, NVIDIA a développé un logiciel appelé CUDA qui fournit une abstraction du matériel sous-jacent, ce qui facilite la portabilité, même à travers les différentes architectures et la compréhension du processeur sous-jacent.

Dans le paradigme GPGPU, le CPU est considéré comme l’hôte et le GPU est utilisé comme un coprocesseur périphérique. De cette manière, le GPU a sa propre mémoire et ses éléments à traiter qui sont séparés de l’ordinateur hôte. Les données doivent être transférées entre l’espace mémoire de l’hôte et la mémoire du GPU pendant l’exécution des programmes. Le transfert de la mémoire du CPU vers la mémoire du GPU est une opération synchrone qui est coûteuse en termes de temps de calcul. Le bus de la bande passante et la latence entre le CPU et le GPU peuvent diminuer significativement les performances de la recherche. Ainsi, ces transferts de données doivent être minimisés [132].

Les opérations séquentielles doivent être programmées comme des fonctions d’hôte qui s’exécutent sur le CPU. Par conséquent, les opérations parallélisables devraient être programmées comme des Kernels ou des fonctions du dispositif qui s’exécutent sur le GPU. Les deux fonctions d’hôte et de dispositif seront appelées via une fonction principale de l’hôte.

3.4. ÉMERGENCE DES GPUS COMME UN OUTIL DE CALCUL GÉNÉRAL

Figure 3.8 – La correspondance entre la physique et la logique de l’architecture GPU Nvidia avec son environnement de programmation CUDA, où DU représente Direction Update, PU représente Position Update, et FU représente Fluence Update.

Dans l’environnement CUDA, l’unité de base d’un code parallèle est le Thread, des milliers de Threads peuvent s’exécuter simultanément avec le même ensemble d’instruc- tions appelé Kernel. Un Thread sur GPU peut être vu comme un élément de donnée à être traité. Comparés aux Threads sur CPU, les Threads sur GPU sont légers. Ce qui signifie que changer le contexte entre deux Threads n’est pas une opération coûteuse. Un Kernel peut employer les registres comme une mémoire à accès rapide. La communication entre les Threads peut être effectuée avec la mémoire partagée, qui est une sorte de mémoire très rapide surtout pour les opérations d’accès de lecture et d’écriture.

Concernant l’organisation spatiale de Threads, les Threads sont organisés à l’intérieur de blocs de Threads. Un Kernel est exécuté par de multiples blocs de Threads de même taille. Les blocs peuvent être organisés en des grilles à une, deux, ou trois dimensions de blocs de Threads, et les Threads à l’intérieur d’un bloc peuvent être regroupés de manière similaire. Tous les Threads appartenant à un même bloc peuvent être affectés à différents multiprocesseurs.

La communication entre le CPU et le GPU peut se faire par la mémoire globale, la mémoire constante, ou la mémoire de texture du GPU. Par ailleurs, il y’a lieu de noter que les dernières générations de GPUs contiennent une mémoire accessible par les deux périphériques.

3.4. ÉMERGENCE DES GPUS COMME UN OUTIL DE CALCUL GÉNÉRAL

Figure 3.9 – L’organisation spatiale de threads.

Après la compilation par l’environnement CUDA, un programme s’exécute comme un Kernel sur le GPU. Un Kernel prend les paramètres d’entrée, effectue les calculs, et renvoi les résultats vers la mémoire du GPU où les résultats peuvent être lus par le CPU. Chaque Thread doit effectuer la même opération dans le Kernel, mais les données d’entrée peuvent être différentes. Avec des milliers de Threads qui font des tâches similaires simultanément, la vitesse de calcul peut s’avérer significativement améliorée. Le CPU est le propriétaire du code hôte qui prépare des données d’entrée et accepte des valeurs de sortie provenant du GPU. La tâche de calcul intensif est gérée par les Kernels GPU. Les données de sortie sont écrites dans la mémoire globale du périphérique (GPU) afin d’être récupérées par le programme du CPU. Pour une description très complète, le lecteur est renvoyé à consulter le guide de programmation CUDA C [26] et des ressources similaires.

Nous pouvons résumer les aspects clés de la haute performance d’un code GPGPU comme ils l’ont été dans l’étude réalisée dans le travail de (yang et al,[205]) :

1. Les accès mémoire doivent être coalescés et chaque élément de données peut avoir besoin d’être un type de vecteur ;

2. L’utilisation commune d’une mémoire partagée se fait par un logiciel qui gère la réutilisation de la mémoire ;

3. Les balances du parallélisme et les optimisations de la mémoire doivent être faites avec soin, comme plusieurs Threads dans le même bloc ainsi que de multiples blocs sont en concurrence pour les ressources limitées dans un SM, y compris le fichier de registre, la mémoire partagée, et le nombre de Threads étant pris en charge dans le matériel ;

4. Des milliers de Threads doivent être exécutées simultanément sur le GPU pour dissimuler la latence de la mémoire ;

5. La divergence de Threads doit être réduite au minimum par l’exécution de tous les Threads au sein d’un Warp en suivant le chemin d’exécution.