• Aucun résultat trouvé

CHAPITRE 2 REVUE DE LITTÉRATURE

2.2 Architectures hétérogènes et processeurs graphiques

2.2.4 Librairies pour le calcul parallèle sur GPU

Dans cette sous-section, nous allons présenter différentes bibliothèques logicielles permettant de programmer un processeur graphique. Nous nous concentrerons sur les solutions pour le traitement général et non graphique.

CUDA

CUDA (Compute Unified Device Architecture) (Kirk, 2007) est une plateforme ainsi qu’une interface de programmation développée par Nvidia depuis 2007. Son apparition a marqué un tournant pour le calcul générique sur processeurs graphiques. CUDA n’est disponible que pour le matériel Nvidia et n’est pas libre de sources. Cette technologie représente néanmoins une solution de référence pour la programmation de cartes graphiques, en proposant d’excellentes performances, de nombreuses optimisations ainsi qu’une certaine facilité d’utilisation. Conçue pour le support de plusieurs langages tels que le C++, C et Fortran, elle offre notamment le principe de "source unique" qui permet d’incorporer directement les noyaux de calcul dans le code source de l’application destiné au CPU.

Du point de vue du fonctionnement, CUDA utilise des queues logicielles appelées Stream qui vont permettre de soumettre des tâches au processeur graphique. On retrouve principalement deux types de tâches :

— Les noyaux de calcul, qui correspondent à des fonctions que le processeur graphique exécutera de manière parallèle sur un ensemble de données.

— Les copies de données multidirectionnelles entre les unités physiques : de la mémoire CPU vers la mémoire GPU ou de la mémoire GPU vers la mémoire CPU

Ces opérations seront, en général, exécutées de manière asynchrone puisque l’utilisateur va contrôler le fait de mettre une opération dans la file d’attente, mais c’est ensuite le processeur graphique lui-même qui s’occupera de l’ordonnancement et exécutera l’opération une fois prêt. Il est possible d’introduire des opérations synchrones, mais cela bloquera le processeur central en attendant que l’opération effectuée sur le processeur graphique soit terminée. Bien moins efficace, ce type d’opérations n’est utilisé que dans certains cas précis.

leur insertion. Afin d’ajouter un mécanisme de concurrence, il est donc intéressant d’utiliser plusieurs Streams. C’est le cas pour les copies de mémoire qui peuvent être réalisées de manière totalement parallèle avec des noyaux de calcul, puisque les composants matériels impliqués sont différents. Cela permettra principalement de masquer la latence introduite par les transferts de mémoire.

Au niveau de l’exécution des noyaux de calcul, CUDA utilise une hiérarchie particulière. On y retrouve des Threads regroupés en blocs appelés Thread blocks qui eux même sont groupés dans une grille ou grid en anglais.

Figure 2.5 Modèle d’exécution de CUDA. La grille et les différents blocs peuvent avoir 1, 2 ou 3 dimensions.

Les blocs ainsi que la grille sont composés de trois dimensions. Au niveau matériel, chaque bloc sera exécuté sur un unique SM mais chaque SM peut néanmoins exécuter différents blocs. Les Streaming Multiprocessors vont toujours créer, gérer, ordonnancer et exécuter les threads sous forme de groupes. Ces derniers seront appelés Warp et peuvent constituer un

sujet important lors d’une phase d’optimisation ou d’analyse de la performance des noyaux de calcul. En effet, une utilisation appropriée peut permettre de réduire la latence d’exécution. De plus, le concept de Warp est utilisé pour définir le taux d’occupation du processeur graphique, ou occupancy en anglais. Il se calcule par le rapport entre le nombre de Warp actifs et le nombre maximal de Warp pouvant être actifs. Ce quotient dépend notamment de la taille des blocs choisie, de l’utilisation de ressources limitées comme les registres, mais aussi de certaines limites matérielles. Bien qu’une valeur de 100% ne soit pas nécessaire, atteindre un haut taux d’occupation est souhaité afin d’utiliser le matériel efficacement.

Un autre élément essentiel lors de la programmation d’une carte graphique concerne la gestion de la mémoire. Comme le montre la figure 2.6, CUDA propose différents types de mémoire

Figure 2.6 Modèle de mémoire utilisé par CUDA

qui correspondent également à des composants distincts au niveau matériel. Chaque mémoire comporte des avantages et inconvénients et une utilisation adéquate et réfléchie est nécessaire afin d’atteindre des performances optimales. En voici une rapide description :

— Mémoire globale : son champ de visibilité est grand puisque tous les threads ainsi que le processeur central y auront accès. Les données y persisteront entre les différents noyaux de calcul au sein d’une même application. L’allocation et la déallocation de mémoire sont gérées manuellement par le programmeur avec des appels explicites à des fonctions comme CudaMalloc et CudaFree. Il s’agit de la mémoire ayant la plus grosse capacité, mais au détriment d’une vitesse relativement faible.

— Mémoire locale ou par thread : sa portée ainsi que sa persistance sont limitées au thread d’exécution. Physiquement, cette mémoire peut être stockée dans deux com-

posants. Tout d’abord dans des registres qui offrent les meilleures performances en termes de vitesse ou dans une mémoire locale, plus lente, quand la taille des registres est insuffisante.

— Mémoire partagée : cette mémoire offre une visibilité pour tous les threads d’un même bloc et une latence plus faible que la mémoire globale. Par ailleurs, sa taille est plus importante que la mémoire locale. Souvent, une utilisation efficace de cette mémoire constituera un point clé pour atteindre une performance maximale.

— Mémoire constante : il s’agit d’un type de mémoire spéciale, de taille fixe et étant disponible uniquement en lecture pour les noyaux de calcul.

Documents relatifs