• Aucun résultat trouvé

D’autres lois ont suivi, telle que la loi de GUSTAFSON[Gustafson, 1988] qui prédit que le gain de vitesse obtenu est proportionnel à la fois au taux que représente la partie non-parallélisable et au nombre de processeurs. La métrique de KARP-FLATT[Karp and Flatt, 1990], proposée en 1990, est plus complexe et efficace que les deux autres lois. Elle intègre le coût lié au temps d’exécutions des instructions qui mettent en œuvre le parallélisme.

2.2.3 Solutions de parallélisation

Avant de paralléliser un programme ou algorithme, il est important de choisir le matériel sur lequel va être implémenté le programme. Il existe plusieurs types d’architectures matérielles dé- diées au parallélisme, chacune d’entre elles possédant des caractéristiques qui lui sont propres (types et taille de la mémoire, vitesse, opérations réalisables, etc.) et appartenant donc à un type de parallélisme particulier (SIMD, MIMD, MISD). Parmi les architectures matérielles les plus connues, on retrouve donc :

— Processeur multi-cœurs : processeur composé de plusieurs unités de calcul "autonomes" permettant d’effectuer plusieurs opérations en parallèle, et donc d’accroître les performances globales du système. Cependant, les programmeurs doivent modifier leurs programmes pour bénéficier des avantages apportés par ces puces.

— Coprocesseur : processeur composé d’unités de calcul spécialisées, c’est à dire dédiées à des traitements particuliers tels que les opérations mathématiques sur les flottants, le cal- cul vectoriel, etc. Les efforts nécessaires à la transformation du code d’un programme pour

2.2. Le parallélisme

prendre en compte cette architecture matérielle sont réalisés en partie par les compilateurs et les bibliothèques dédiées.

— Carte graphique (GPU, de l’anglais Graphics Processing Unit) : processeur composé de cen- taines (voir de milliers) de cœurs dédiés, à la base, au calcul et rendu graphique. Ils sont cependant de plus en plus utilisés pour faire du calcul généraliste. Cette technologie néces- site une transformation complète du code du programme pour bénéficier des performances offertes par ces cartes.

Cependant, de par la spécificité de ces architectures matérielles, les programmes doivent être entièrement pensés dans l’optique d’être utilisés sur ce matériel particulier : l’implémentation d’un programme est directement impactée par le choix du matériel sur lequel il va être exécuté. De plus, un programme réalisé pour une architecture spécifique n’est pas compatible avec une autre et ne peut que très rarement être réutilisé. En effet, paralléliser un programme sur plusieurs unités de traitements (pour un processeur multi-cœurs par exemple) est très différent d’une pa- rallélisation sur une carte graphique possédant des milliers d’unités de traitement. Enfin, les per- formances offertes ainsi que l’efficacité énergétique de ces différentes architectures varient énor- mément sans forcément se valoir. Faire le bon choix d’architecture est donc essentiel.

Une fois le matériel choisi, il est nécessaire d’utiliser un langage de programmation adapté à la programmation parallèle2. Parmi les plus connus, nous pouvons en présenter quatre :

— OpenMP3(Open Multi-Processing) est une interface de programmation pour le calcul pa- rallèle sur architecture à mémoire partagée. OpenMP est portable et permet de développer rapidement des applications parallèles en restant proche du code séquentiel.

— MPI4(The Message Passing Interface) est une interface de programmation dédiée au calcul parallèle aussi bien sur des machines massivement parallèles à mémoire partagée que sur des clusters d’ordinateurs hétérogènes à mémoire distribuée. MPI est grandement portable (car MPI a été implémenté sur presque toutes les architectures de mémoire) et rapide (car chaque implémentation a été optimisée pour le matériel sur lequel il s’exécute).

— OpenCL5(Open Computing Language) est la combinaison d’une API (de l’anglais Applica-

tion Program Interface) et d’un langage de programmation dérivé du C, proposé comme un standard ouvert par le Khronos Group. OpenCL est conçu pour programmer des systèmes parallèles hétérogènes comprenant par exemple à la fois un CPU multi-cœurs et un GPU. — CUDA6(Compute Unified Device Architecture) est aussi une combinaison entre une API et

un langage de programmation dérivé du C. Il est dédié à la programmation sur GPU, c’est- à-dire qu’il permet d’utiliser un processeur graphique (GPU) pour exécuter des calculs gé- néraux habituellement exécutés par le processeur central (CPU).

2.2.4 Les cartes graphiques : une alternative intéressante pour le calcul intensif

Les processeurs multi-cœurs et les coprocesseurs font partis des architectures matérielles pri- vilégiées pour faire du calcul intensif. Cependant, bien qu’offrant de très bonnes performances [Hamidouche et al., 2009], ces solutions n’offrent pas toujours d’outils d’assez haut niveau pour être exploitables massivement par les programmeurs [Bourgoin, 2013]. De plus, l’investissement nécessaire pour développer une application sur ce type d’architecture (achat du matériel et temps de développement) ainsi que le coût d’exploitation (consommation énergétique) peut vite devenir très important.

2. De nombreux langages de programmation, au départ séquentiel (Java, C++, etc.), permettent de faire ce que l’on appelle du multi-tâches et donc de profiter, en partie, de l’architecture parallèle des CPU. Ici, nous considérons uniquement les langages de programmation dédiés au calcul intensif.

3. https://www.openmp.org

4. https://www.open-mpi.org

5. https://www.khronos.org/opencl/

Ces contraintes ont poussé à étudier d’autres solutions, comme la possibilité d’utiliser les cartes graphiques pour faire du calcul intensif. En effet, ces cartes sont déjà bien connues dans le monde du graphisme et offrent de très bonnes performances [Owens et al., 2007, Che et al., 2008]. De plus, de nos jours, tout ordinateur personnel est équipé d’une carte graphique. Enfin, ces cartes possèdent un autre avantage non négligeable : leur prix très accessible.

Ainsi, c’est leur rapport prix/performance et leur disponibilité qui nous ont poussés à choisir cette plateforme matérielle pour nos développements et implémentations. Cependant, la techno- logie associée, appelée GPGPU (de l’anglais General-Purpose computing on Graphics Processing Units, qui permet de faire du calcul intensif sur ces cartes), s’appuie sur un parallélisme de type SIMD. Très différent d’une approche de programmation séquentielle classique, ce type de parallé- lisme qui nécessite notamment de suivre les principes de la programmation par traitement de flot de données est très spécifique et complexe. Nos travaux de thèse s’appuyant en grande partie sur l’utilisation de cette technologie, nous proposons maintenant d’introduire en détails le GPGPU.

2.3 General-Purpose computing on Graphics Processing Units

Une carte graphique repose sur une architecture matérielle many-core proposant un très grand nombre de cœurs d’exécution. Très rapidement, l’intérêt d’utiliser ces cartes pour faire du HPC est venu du fait qu’elles sont faites pour être performantes en calcul numérique (e.g. calcul géomé- trique dans les jeux vidéo, calcul matricielle pour le graphisme) et offrent un rapport performan- ce/coût supérieur aux autres solutions existantes (voir tableau 2.1). Cependant, effectuer du calcul généraliste n’a pas toujours été un des rôles des GPU. Comme nous allons le voir, ces architectures ont énormément évoluées avec le temps pour devenir, aujourd’hui, de véritables plateformes dé- diées au calcul intensif.

CPU GPU Desktop GPU HPC

Core i7-4790 Core i7-6700K Geforce GTX 980 Tesla K80 # Unités de calcul 4 (8 Threads) 4 (8 Threads) 2 048 4 992

Fréquence 3.6 GHz 4.0 GHz 1.2 GHz 875 MHz

Mémoire Max 32 GB 64 GB 4 GB 24 GB GFlops (simple précision) 27.15 43.83 4 612 8 736

GFlops (double précision) 15.34 22.76 144 2 912

Prix 325 € 425 € 520 € 5 000 €

TABLEAU2.1 – Comparaison entre les caractéristiques des CPU et GPU.

2.3.1 Historique et évolution des capacités des GPU

Dans les années 1980, les cartes graphiques ont été développées avec, comme objectif premier, de soulager les processeurs (CPU) de la charge que demandait l’affichage du texte et, plus tard, la gestion graphique de chaque pixel de l’écran. Ces cartes sont des processeurs (GPU) secondaires, dotés de leur propre espace mémoire, connectés et contrôlés par le CPU.

Avec l’engouement des années 90 pour les graphismes dans le jeu vidéo (couleur, animations et nombre de pixels à afficher de plus en plus important), ces cartes ont rapidement été amenées à traiter de plus en plus d’opérations (déléguées par le CPU) et se sont donc perfectionnées jusqu’à devenir des GPU programmables capables d’exécuter des programmes spécialement écrits pour eux. Ces capacités de programmation sont très limitées et ne sont accessibles que par le biais de bibliothèques de rendu graphique comme OpenGL7 (de l’anglais Open Graphics Library) et DirectX8.

C’est au début des années 2000 que le GPGPU commence à être utilisé. Il n’existait alors au- cune interface de programmation spécialisée et les rares personnes intéressées détournaient donc

7. https://www.opengl.org

2.3. General-Purpose computing on Graphics Processing Units

"à la main" les fonctionnalités graphiques de la carte pour réaliser des calculs relevant d’un autre contexte [Owens et al., 2007]. Par exemple, il était nécessaire d’utiliser les textures graphiques comme structures de données : chaque texel9de la texture permettait de stocker 4 variables (de 8

bits) à la place des valeurs usuelles rouge, bleu, vert et alpha. Ces données étaient ensuite traitées et manipulées par des scripts, les shaders10, exécutés par le GPU. Du fait de la complexité associée à cette utilisation non conventionnelle du GPU, celle-ci est longtemps restée réservée à une petite communauté de programmeurs.

Il aura fallu attendre 2007 pour que sorte la première interface de programmation dédiée au GPGPU. Mise à disposition par Nvidia, CUDA a pour objectif de fournir un meilleur support et une plus grande accessibilité à cette technologie. Ainsi, CUDA fournit un environnement logiciel de haut niveau permettant aux développeurs de définir et contrôler les GPU de la marque Nvi- dia par le biais d’une extension du langage C. Cette solution a été suivie en 2008 par OpenCL, un framework de développement permettant l’exécution de programmes sur des plateformes maté- rielles hétérogènes (un ensemble de CPU et/ou GPU sans restriction de marques). OpenCL est un standard ouvert, fourni par le consortium industriel Khronos Group, qui s’utilise aussi comme une extension du langage C. CUDA et OpenCL sont actuellement les deux solutions les plus populaires pour faire du GPGPU.

De nos jours, l’utilisation des GPU comme architecture dédiée au calcul intensif devient si po- pulaire que la marque Nvidia communique de plus en plus sur les capacités en terme de puissance de calcul de ses cartes alors que cette communication était auparavant axée sur les capacités gra- phiques dans les jeux vidéo.

2.3.2 Une nouvelle architecture d’exécution

Afin de développer les capacités GPGPU des cartes graphiques, les GPU ont évolué et sont maintenant composés de centaines d’ALU (de l’anglais Arithmetic Logic Units) formant une struc- ture hautement parallèle capable de réaliser des tâches plus variées. Ces ALU ne sont pas très ra- pides (beaucoup moins qu’un CPU) mais permettent d’effectuer des milliers de calculs similaires de manière simultanée. Une des grandes différences entre l’architecture CPU et l’architecture GPU vient donc du nombre de cœurs d’exécution (d’ALU) qui composent un GPU, bien plus important que pour un CPU, comme le montre la figure 2.4.