Spéculation temporelle sûre pour réseaux de neurones convolutifs 

Télécharger le fichier pdf d’un mémoire de fin d’études

Ordonnancement dynamique

Un moyen de limiter l’impact des aléas sur l’IPC est de réorganiser les instructions afin d’éviter les stalls : c’est l’ordonnancement dynamique. Le processeur n’exécute alors plus les instructions dans l’ordre spécifié par le programme mais dans le désordre (Out-of-order execution, OOO). Pour cela, il gère une fenêtre d’instructions dans laquelle une instruction peut être exécutée dès que ses opérandes sont prêts. Par exemple, en cas de défaut de cache, des instructions ne dépendant pas de cet accès mémoire pourront être exécutées pendant le temps d’attente. Cependant, le processeur doit toujours s’assurer de respecter la sémantique du programme. Les instructions arithmétiques peuvent donc être réorganisées en respectant les diverses dépendances, mais pas les instructions d’accès à la mémoire et les branchements.
L’ordonnancement dynamique permet d’éviter les stalls mais a un coût important en ressources et en énergie à cause de sa complexité.

Exécution spéculative

Bien que l’exécution OOO permette d’augmenter l’IPC, celui-ci est limité par les bran-chements et les instructions d’accès à la mémoire. Les premières limitent l’IPC parce que le processeur ne peut réordonner uniquement les instructions qu’il est certain d’exécuter. Les instructions rentrent dans la fenêtre d’exécution seulement lorsque les branchements sont résolus. Les secondes parce que exécuter les accès à la mémoire dans l’ordre permet de s’assurer que la sémantique du programme (basée sur l’exécution dans l’ordre) est respectée.
L’exécution spéculative permet de lever ces limitations : le principe est d’autoriser l’exécution d’instructions qui peuvent être nécessaires et d’exécuter les instructions avec accès mémoire dans le désordre.
Pour les branchements, une branche est considérée comme prise et la fenêtre d’ins-tructions est remplie avec les instructions de cette branche, permettant leur exécution dans le désordre. Le processeur enregistre les instructions terminées dans une mémoire, le Re-Order Buffer (ROB). Finalement, le processeur sait si la branche prise était la bonne lors de la résolution du branchement. Dans ce cas, il valide les instructions cor-respondantes dans le ROB, dans l’ordre original du programme. Dans le cas contraire, il les annule, ainsi que la fermeture transitive des instructions ayant utilisé des résultats d’instructions invalides. Un mécanisme de prédiction de branchement permet de choisir la branche considérée comme prise selon diverses heuristiques afin d’augmenter le niveau l’IPC effectif.
Les opérations de lecture et d’écriture en mémoire des instructions sont gérées via une mémoire, la Load-Store Queue (LSQ), permettant de déterminer si des dépendances mé-moires ont été violées à cause de l’exécution dans le désordre. Cette mémoire enregistre les adresses des données accédées ainsi que leur valeur (dans le cas l’écriture) lors de l’exécution spéculative des instructions d’écriture (store) et de lecture (load). Les lectures récupèrent les données depuis la LSQ si une écriture correspondante a eu lieu et que cette écriture précède la lecture dans l’ordre original du programme, sinon depuis la mémoire.
Les écritures sont valides, sauf si une lecture correspondante suivant l’écriture dans l’ordre original a déjà eu lieu. Dans ce cas, l’instruction de la lecture ainsi que la fermeture tran-sitive des instructions ayant utilisé cette valeur sont annulées grâce au ROB et réinjectées dans la fenêtre d’exécution afin de les exécuter à nouveau ultérieurement. Les écritures spéculatives sont reportées en mémoire uniquement lorsque l’instruction correspondante est validée, permettant leur exécution séquentielle conformément à la sémantique du pro-gramme. Lors de la validation d’une instruction, les informations associées dans la LSQ sont retirées.
Tout comme l’ordonnancement dynamique, l’exécution spéculative augmente l’IPC mais a un coût important en mémoire, en logique et en énergie. Michaud et al. ont observé empiriquement que le nombre d’instructions exécutables parallèlement est proportionnel à la racine carrée de la taille de la fenêtre (Michaud, Seznec et Jourdan 2001). Aug-menter l’IPC exploitable via l’exécution spéculative est donc de plus en plus coûteux, plus que linéairement, pour le niveau d’IPC recherché.

Jeu d’instructions vectoriel

Plutôt que d’exploiter le parallélisme intrinsèque à un programme en l’analysant dy-namiquement, il est possible d’exposer celui-ci explicitement dans les instructions. Dans ce cas, c’est le compilateur et non le processeur, qui extrait le parallélisme du programme par des analyses statiques. Il faut alors que le jeu d’instructions du processeur soit conçu pour exprimer ce parallélisme.
Certains jeux d’instructions (comme les familles x86 et ARM) ont été étendus par des extensions vectorielles. Ces extensions (MMX, SSE, AVX, 3DNow!, NEON, etc.) ajoutent des instructions permettant de traiter des ensembles (vecteurs) de données, plutôt qu’une donnée unique. Par exemple, les extensions AVX-512 peuvent traiter des vecteurs de 512 bits, soit par exemple 16 nombres à virgule flottante simple précision, 64 entiers 8 bits, etc. Les calculs d’une instruction peuvent alors être traités efficacement par la microarchitecture du processeur et ordonnancés (statiquement) sur les ALU du processeur. Elle permet aussi d’améliorer la récupération des données en mémoire en requérant parfois que les données vectorisées soient contigües et alignées et en augmentant la densité des données « utiles » dans le programme (moins d’instructions sont utilisées pour autant d’adresses de données). La figure 1.3 donne un exemple de programme assembleur mettant en œuvre une telle extension pour un type de calcul intensément utilisé pour certains algorithmes d’intelligence artificielle.

Hiérarchie mémoire
Comme que les processeurs peuvent exécuter plus d’instructions par seconde, l’ac-cès à la mémoire devient plus critique pour les performances. En effet, un processeur, ou n’importe quelle plateforme de calcul, a besoin d’accéder aux données à traiter (et éventuellement au programme à exécuter), stockées dans une mémoire. Or, les perfor-mances des mémoires s’améliorent plus lentement que celles des processeurs : l’écart se creuse d’année en année. Pour limiter l’impact de la mémoire sur les performances des processeurs, les solutions doivent donc être architecturales (Wulf et McKee 1995).
La mémoire doit optimiser plusieurs métriques indépendantes :
— la capacité : la quantité d’information stockée ;
— la latence : le temps d’attente pour accéder à une information ;
— le débit : la quantité d’information transférée par unité de temps ;
— la puissance électrique nécessaire ;
— le coût.
Aucune technologie de mémoire n’est optimale sur tous ces points. Au contraire, elles offrent différents compromis. Une manière d’obtenir une mémoire avec de bonnes perfor-mances sur toutes les métriques est d’utiliser une hiérarchie mémoire.
Plutôt que d’implémenter une unique mémoire avec une seule technologie, une hiérar-chie mémoire utilise plusieurs mémoires de différentes tailles tirant profit des avantages des différentes technologies. Ces mémoires sont organisées en niveaux hiérarchiques. Chaque niveau a une plus grande capacité, une plus grande latence, un plus faible débit, une plus faible puissance et un plus faible coût (par octet) que le niveau précédent. Si une donnée requise est absente d’un niveau (miss), elle est (récursivement) rapatriée depuis le niveau supérieur. Si la donnée est présente (hit), il n’y a pas besoin d’utiliser le niveau supérieur. Les données modifiées ou créées sont aussi reportées au niveau supérieur lorsque c’est né-cessaire. Chaque niveau contient donc un sous-ensemble, potentiellement mis à jour, des données du niveau supérieur. Le temps d’accès à une donnée dépend donc directement du niveau le plus bas dans lequel la donnée se trouve.
La plupart des programmes ou applications n’accèdent pas aux données uniformé-ment mais tendent à réutiliser les données et instructions utilisées précédemment (localité temporelle) et les données et instructions adjacentes (localité spatiale) (Hennessy et Patterson 2011). Ces propriétés sont exploitées pour augmenter la probabilité de trou-ver une donnée dans un niveau faible de la hiérarchie lorsque le processeur en a besoin. Les données sont copiées dans un niveau inférieur par lignes, en copiant donc une certaine quantité de données adjacentes à la donnée requise qui peuvent potentiellement être utiles. Certains processeurs implémentent aussi un prefetcher pour copier les données avant que le processeur ne les requiert en spéculant sur le fait qu’elles seront utilisées (par exemple, en copiant la ligne suivant la ligne requise). Les données et instructions utilisées ont donc plus de chances de se trouver dans les niveaux inférieurs : malgré quelques temps d’ac-cès longs, le temps d’accès moyen peut être beaucoup plus faible que les temps d’accès des niveaux supérieurs. Grâce à la localité, la hiérarchie mémoire permet donc d’obtenir virtuellement une mémoire ayant une faible latence et un haut débit grâce aux premiers
Par exemple, dans un processeur moderne, la hiérarchie mémoire couvre typiquement le fichier de registres 3 (temps d’accès inférieur à un cycle d’horloge), deux ou trois niveaux de caches et la mémoire vive extérieure au processeur, comme illustré par la figure 1.4. On peut aussi considérer le stockage de masse, non volatile, comme faisant partie de la hiérarchie mémoire avec une taille pouvant aller jusqu’à plusieurs téraoctets et des temps d’accès de 50 µs pour les mémoires flash à 10 ms pour les disques durs. La plage de temps d’accès recouvre donc huit ordres de grandeur et celle des capacités dix ordres de grandeur.
La hiérarchie mémoire a un impact important sur les performances des processeurs, mais aussi sur leur puissance électrique et leur efficacité énergétique. Par exemple, Dally et al. montrent que 70 % de l’énergie d’un processeur embarqué est utilisée pour gérer les données et instructions, alors que seule 6 % est utilisée pour réaliser les calculs (voir le détail figure 1.5) (Dally et al. 2008). Au total, l’énergie requise pour exécuter une instruction est 15 à 50 fois l’énergie utilisée pour réaliser les calculs.
Architectures explicitement parallèles
Pour obtenir de meilleures performances dans les processeurs basés sur un jeu d’ins-truction utilisant une sémantique séquentielle (comme la famille x86), le processeur doit lui-même essayer d’exploiter l’ILP en analysant le programme et en ordonnançant au mieux les instructions selon les capacités de sa microarchitecture. Comme vu précédem-ment, cette approche a un coût énergétique élevé.
L’exploitation du parallélisme d’un programme et l’ordonnancement des instructions peuvent être relégués à la phase de compilation (par opposition à la phase d’exécution). La plateforme est alors explicitement parallèle : son jeu d’instruction permet au compilateur d’exprimer explicitement le parallélisme d’instruction. Cette section présente quelques machines explicitement parallèles.
Processeur à jeu d’instruction explicitement parallèle
Les processeurs Very Long Instruction Word (VLIW) utilisent un jeu d’instructions spécialisé permettant d’exposer le parallélisme des instructions : à chaque cycle, le pro-cesseur exécute un paquet (bundle) d’instructions. Chaque instruction est exécutée sur une unité d’exécution prédéfinie (dont certaines peuvent être spécialisées, par exemple en gérant la mémoire ou les opérations arithmétiques complexes). Le compilateur doit donc ordonnancer les instructions du programme bundle par bundle en essayant de paralléliser au mieux. La figure 1.6 montre un pipeline d’un VLIW.
Exposer explicitement le parallélisme des instructions permet d’obtenir une meilleure efficacité énergétique. En effet, le processeur est rendu plus simple : il n’a pas à calculer les dépendances entre instructions, à spéculer, etc. La compilation, elle, devient plus difficile mais le compilateur a un budget de temps et d’énergie pour trouver le meilleur ordonnan-cement possible bien supérieur au budget dans le cas de l’ordonnancement dynamique.
Cependant, en pratique, toutes les unités d’exécution ne sont pas utilisées à chaque cycle. De plus, l’ordonnancement statique ne peut pas réagir à des évènements tels que des cache miss, contrairement à l’ordonnancement dynamique. Enfin, un programme compilé pour une architecture de VLIW sera incompatible avec d’autres VLIW, même si un seul paramètre change (nombre de voies, profondeur du pipeline, spécialisation des voies, etc.) :
il n’est pas portable. Contrairement à un processeur généraliste qui expose la même ISA quelle que soit sa microarchitecture, le programme pour VLIW devra être compilé spéci-fiquement pour chaque architecture. Ce type de processeur convient donc pour exécuter des noyaux d’exécution très optimisés.
Processeur graphique
Les processeurs graphiques (Graphics Processing Unit, GPU) ont une architecture hautement parallèle. Ils étaient spécialisés à l’origine dans les calculs graphiques. Ce type de calcul nécessite d’appliquer les mêmes suites de traitement à de nombreuses données (par exemple, des coordonnées 2D ou 3D (vertex), les pixels d’une image, etc.), en utilisant principalement une arithmétique à virgule flottante. Les GPU ont donc une architecture adaptée à ce type de calcul. Un GPU ne remplace pas un processeur mais est utilisé pour accélérer certains calculs.
Un GPU contient de nombreuses unités de calcul (jusqu’à plusieurs milliers) et sa propre hiérarchie mémoire (caches et mémoire vive). Ces unités d’exécution sont organisées en groupes (Streaming Multiprocessor 4 (SM)). Les cœurs d’un SM exécutent le même programme sur des données différentes. Le contrôle (comme le décodage des instructions) peut alors être partagé pour plusieurs cœurs, ce qui permet d’utiliser plus de ressources pour les calculs « utiles ». Par exemple, l’architecture Fermi (« NVIDIA’s Next Generation CUDA Compute Architecture » 2009) contient 16 SM de 32 cœurs. La figure 1.7 illustre l’architecture simplifiée d’un GPU comparée à celle d’un processeur.
Les termes utilisés reprennent la terminologie CUDA.
Ce modèle d’exécution est de type instruction unique, thread multiples (Single Instruc-tion, Multiple Threads, SIMT) (Lindholm et al. 2008). Les noyaux de calculs (kernels) à exécuter sur le GPU sont organisés en blocs de threads (fils d’exécution). Les threads d’un bloc exécutent les mêmes instructions sur différentes données (chaque bloc est exé-cuté par un SM, chaque cœur exécutant un thread). Les blocs peuvent être exécutés dans n’importe quel ordre, séquentiellement ou parallèlement. Leur synchronisation est cepen-dant possible à certains points grâce à des barrières. Contrairement au modèle SIMD, les threads peuvent contenir des branchements et les branches prises peuvent différent entre les threads d’un même bloc. La gestion de la mémoire est critique pour les performances. En effet, les données doivent être transférées explicitement depuis la mémoire de l’hôte, puis l’inverse. Les copies mémoire et les calculs sont recouverts pour cacher la latence. Un GPU a donc un débit élevé, mais une latence relativement importante. Le programme à accès à une mémoire par thread, une mémoire partagée par bloc et une mémoire globale. Les accès mémoire des threads sont regroupés si possible pour minimiser le nombre de transactions. Un programme performant doit donc minimiser le nombre de transactions en organisant correctement sa mémoire et en utiliser les caches.
Si cette architecture convient bien aux calculs graphiques, les GPU ont commencé à être utilisés les années 2000 pour du calcul généraliste (calcul générique sur proces-seur graphique (General-Purpose Computing on Graphics Processing Units, GPGPU)) et sont devenus plus programmables via des interfaces de programmation comme CUDA et OpenCL. CUDA permet d’exploiter des GPU depuis un langage de programmation usuel comme C++, malgré le modèle d’exécution très différent d’un processeur.
Les GPU sont performants pour les applications de calcul intensif : rendu 3D, calcul scientifique, applications multimédias, intelligence artificielle. Ils sont pertinents pour les applications qui nécessitent un haut débit et peu de réutilisation de données. En revanche, la latence relativement importante peut être problématique pour certaines applications, comme parfois en intelligence artificielle où le résultat doit être connu le plus rapidement possible. Ils sont plus efficaces en énergie que les processeurs généralistes pour les calculs en virgule flottante avec 225 pJ contre 1700 pJ par opération en virgule flottante. Au sein d’une même génération, les GPU nécessitent généralement plus de puissance que les processeurs (par exemple 365 W pour un GPU GeForce GTX 509 contre 45 W pour un processeur Intel i7 3770T, soit 8 fois plus) mais peuvent, selon les applications, avoir une meilleure efficacité énergétique (Mittal et Vetter 2014).
Processeur spécialisé pour l’intelligence artificielle
Si les GPU sont utilisés dans le domaine de l’intelligence artificielle, les importants besoins en performance et la spécificité de certains calculs de ce domaine ont poussé plusieurs sociétés à développer des circuits spécialisés dans ce domaine comme les Tensor Processing Unit (TPU) de Google. Déployée en 2015, la première version permet de réaliser une inférence de réseau de neurones profond 15 à 30 fois plus vite qu’un processeur ou GPU avec une efficacité énergétique 30 à 80 fois supérieure (Jouppi et al. 2017). Ces niveaux de performance sont atteints grâce à la spécialisation du circuit.
Le cœur du circuit est une grille de 256×256 opérateurs de multiplication-accumulation capable de fournir un débit de 92 Top s−1. Ces opérateurs travaillent sur des entiers 8 bits, une précision suffisante pour les applications cibles, car ce format permet de meilleures performances et une meilleure efficacité énergétique. En effet, la multiplication entière 8 bits nécessite 6 fois moins d’énergie et 6 fois moins de ressources matérielles que la multiplication en flottant IEEE 754 16 bits et l’addition 13 fois moins d’énergie et 38 fois moins de ressources matérielles (Jouppi et al. 2017). La multiplication de matrice est organisée en un réseau systolique (Quinton et Robert 1989) tel qu’illustré figure 1.8.
Cette organisation permet de transférer les données directement d’un opérateur à un autre, simplifie le contrôle et évite les lectures en mémoire SRAM coûteuses en énergie. Le réseau systolique représente 24 % de la surface du circuit et les différents tampons 35 %. Le contrôle ne représente que 2 % de la surface du circuit, beaucoup moins que pour un processeur ou un GPU (Jouppi et al. 2017). Le TPU contient aussi un circuit spécialisé pour d’autres calculs utilisés dans les réseaux de neurones, comme l’activation, la normalisation, le pooling, etc.
Le TPU fonctionne grâce à un processeur hôte qui le programme grâce à une dou-zaine d’instructions spécifiques. Ces instructions ont un niveau d’abstraction beaucoup plus élevé que les instructions classiques de processeur, comme calculer une multiplication de matrice de telle taille sur les données à telles adresses. La mémoire est gérée explici-tement entre le système hôte et le TPU. Un programmeur peut cependant l’utiliser plus simplement via une interface de programmation plus abstraite comme Tensorflow (Abadi et al. 2016).
Discussion
Grâce au parallélisme explicite, la machine n’a plus besoin de certains mécanismes coûteux comme une unité d’exécution dans le désordre et cela permet en général d’obtenir une meilleure efficacité énergétique. Grâce à la meilleure efficacité énergétique, il devient possible d’augmenter le nombre d’unités de calculs tout en restant sous les contraintes de puissance et d’enveloppe thermique. Ainsi le TPU contient 65 536 unités de calcul spécialisées pour réaliser des multiplications de matrice ; un GPU peut contenir plusieurs milliers de cœurs et le processeur manycore MPPA-256 contient 256 cœurs VLIW comme celui présenté ci-dessus. Ces machines permettent donc d’obtenir de bonnes performances pour les applications hautement parallèles. Un GPU et un VLIW restent des machines programmables, basées sur un jeu d’ins-truction généralistes (le GPU, s’il est spécialisé, contient par exemple des instructions de branchement). Au contraire, le TPU est spécialisé dans une seule tâche : réaliser les calculs nécessaires pour l’inférence d’un réseau de neurones. Il n’est pas possible d’utiliser un TPU pour d’autres tâches et son « jeu d’instruction » est très limité. C’est une archi-tecture dédiée. Cette spécialisation permet au TPU d’être plus performant qu’un GPU et d’avoir une meilleure efficacité énergétique pour cette tâche.
Dans la section suivante, nous présentons les architectures dédiées d’une manière plus générale.
Architectures dédiées
L’objectif d’une architecture dédiée est d’améliorer les performances ou l’efficacité énergétique d’une plateforme pour une tâche spécifique. Intuitivement, une architecture dédiée utilise les mêmes éléments de base qu’une architecture généraliste (hiérarchie mé-moire, bus mémoires, unités de calculs, etc.) mais une plus grosse partie de ces éléments est utilisée pour les calculs « utiles » à la tâche. Ainsi, à l’instar du TPU, les performances et l’efficacité énergétique sont meilleures que sur une machine généraliste pour la tâche choisie.
En revanche, le coût du développement d’une telle architecture jusqu’à la production de circuits électroniques (Application-Specific Integrated Circuit (ASIC)) est beaucoup plus élevé que le coût du développement d’une solution logicielle, ainsi que beaucoup plus long (quelques mois voire années). Il faut que les gains espérés (en performance, coût de fonctionnement, volume de vente, etc.) soit suffisamment importants pour que le développement d’une architecture dédiée soit intéressant. Les Field-Programmable Gate Array (FPGA), circuits reconfigurables, offrent un compromis entre le coût et le temps de développement, et la performance et l’efficacité énergétique. Ils permettent en effet d’éviter les dernières étapes du développement et la phase très coûteuses d’un ASIC. S’ils sont utilisés dans la phase de prototypage des ASIC, on les trouve aussi dans les produits finaux grâce à leur coût plus faible (par exemple en cas de faible volume) et leur aspect reconfigurable (ils permettent des corrections, des évolutions, etc.).
Architecture des FPGA
Les FPGA sont des composants électroniques dont le fonctionnement peut être modi-fié par configuration : on dit qu’ils sont reconfigurables, ou encore qu’il s’agit de logique programmable. Ils sont basés sur une grille de composants relativement simples et d’un réseau d’interconnexion. Le comportement de chaque composant, ainsi que leur intercon-nexion, peut être modifié par quelques bits de configurations. La reconfiguration permet ainsi d’obtenir n’importe quel circuit d’électronique numérique tant que les ressources présentes sont suffisantes. Un FPGA peut donc être vu comme un circuit prêt à l’emploi permettant d’implanter des architectures spécifiques.
Le composant principal est la lookup Table (LUT). Celle-ci permet le calcul de n’im-porte quelle fonction booléenne : les bits de configuration déterminent la fonction boo-léenne qui sera calculée sur les entrées de la LUT selon le principe d’une table de vérité.
Les LUT sont généralement de 4 ou 6 entrées et une sortie, nécessitant 16 ou 64 bits de configuration.
Les LUT sont regroupées dans des composants appelés slices (ou logic elements). Les slices contiennent plusieurs LUT, des bascules, ainsi que la logique suffisante pour réaliser efficacement des opérations communes (un registre à décalage, de la mémoire ou la retenue d’une addition) tout en restant, encore une fois, génériques.
Les FPGA modernes contiennent aussi des composants plus spécialisés implémentant des fonctions couramment utilisées. Par exemple, des multiplieurs et des additionneurs entiers câblés (Digital Signal Processing, DSP) ou des blocs mémoire (BRAM). Si ces composants sont toujours configurables pour être utilisés de différentes manières, leur spécialisation permet de réduire les ressources nécessaires par rapport à une implémenta-tion avec des LUT, mais aussi d’augmenter les performances et l’efficacité énergétique de ces fonctions communes. Les fabricants de FPGA tentent de trouver un équilibre entre ces composants plus spécialisés et efficaces et les composants génériques pour convenir à tout type d’application.
L’interconnexion permet de router l’information, au niveau bit, entre les différents composants via des switch matrix configurables. Enfin, les FPGA contiennent des éléments gérant les entrées/sorties (IOB), des boucles à phase asservie pour générer les signaux d’horloges, un circuit permettant sa configuration, etc. La figure 1.9 illustre l’agencement des principaux éléments pour un petit FPGA.
Les circuits logiques programmables proposent un grain de configuration très fin. Ils permettent aux concepteurs de configurer les éléments logiques pour réaliser n’importe quelle fonction logique ; stocker et router les informations au niveau bit ; gérer directe-ment les entrées/sorties ; mais aussi utiliser plusieurs horloges et configurer leur fréquence ; etc. Autrement dit, la couche d’abstraction au-dessus des composants électroniques élé-mentaires est assez faible. L’utilisation des FPGA n’est donc pas limitée à un usage de calculateur numérique spécialisé : ils peuvent aussi être utilisés pour prototyper ou réa-liser des circuits spécifiques exploitant des caractéristiques des circuits électroniques. Ils permettent donc d’implémenter des types d’application qui ne sont pas réalisables sur d’autres architectures (si elles ne contiennent pas déjà des composants spécifiques néces-saires), comme certains générateurs de nombres aléatoires ou la détection du temps de propagation d’un signal.
Modèle de programmation
Si l’architecture matérielle d’un FPGA est régulière et relativement simple, la com-plexité de la logique programmable se trouve dans la description du circuit considéré et dans la génération de la configuration. Celle-ci, appelée bitstream, est générée par une chaîne de traitement, à l’instar de la compilation.
La description du comportement attendu du FPGA est écrite dans un langage de des-cription de matériel (Hardware Description Language, HDL). Les HDL, comme le VHDL ou le Verilog, utilisent l’abstraction Register Transfer Level (RTL) pour décrire des archi-tectures matérielles. Cette abstraction permet de décrire un circuit électronique synchrone en se basant sur les registres et les fonctions logiques combinatoires. En particulier, cette abstraction n’utilise pas une sémantique séquentielle comme les langages de programma-tions, mais décrit des comportements. Elle permet notamment de décrire plus facilement des comportements parallèles.
Plusieurs phases transforment la description de matériel en bitstream. Les outils de synthèse logique produisent d’abord une implémentation au niveau porte logique, la netlist. Cette première phase contient, entre autres, des simplifications logiques afin de réduire le nombre de portes logiques nécessaires puis la génération du circuit à base de portes logiques. La netlist est ensuite projetée sur les ressources disponibles (slices, DSP, BRAM, etc.) selon les contraintes du FPGA cible (entrées/sorties, délais, etc.). Enfin, la phase de placement et routage place ces ressources sur l’architecture du FPGA cible et trouve le routage des signaux nécessaires. Cette phase doit respecter les contraintes architecturales, les contraintes de délai, les contraintes de puissance, etc. tout en essayant d’obtenir le meilleur résultat possible. Finalement, le bitstream peut être généré. La description de matériel est complexe : les HDL sont « bas niveau » et offrent peu d’abstractions. Afin d’améliorer la productivité lors de la création d’un circuit, les outils de synthèse de haut niveau (High-Level Synthesis, HLS) permettent d’utiliser un niveau d’abstraction plus élevé. Ces outils analysent un programme écrit dans un langage « haut niveau » (comme C, C++, Matlab ou certains langages spécifiques comme SystemC) et génèrent une description de matériel correspondante en HDL. Ils transforment donc une description algorithmique du matériel en une description comportementale. La transfor-mation se base sur une analyse sémantique, des optimisations spécifiques pour le matériel, la recherche de parallélisme, l’ordonnancement des opérations, etc.
Exploration de l’espace de conception
Le niveau de flexibilité dans la configuration des FPGA permet de réaliser des ar-chitectures dédiées pour les besoins d’une application et offrent de nombreux choix de conception. Par exemple, une architecture dédiée peut utiliser n’importe quelle largeur de donnée au bit près ; des opérateurs spécialisés (multiplications par des constantes (F. de Dinechin et al. 2019), opérateurs approchés (Sentieys et al. 2021), etc.) ; des mémoires de la taille nécessaire et avec autant d’accès que nécessaire ; etc. Certains choix portent sur l’organisation des calculs (tailles des blocs de calcul, flots de données, réutilisation des opérateurs et des données, pipeline, architectures systoliques, etc.) voire sur les différents algorithmes répondant au besoin (Y. Liang et al. 2019).
Les choix de conception ajoutent des degrés de liberté qui augmentent la taille de l’espace de conception, l’ensemble des solutions possibles. Tous les points de cet espace ne sont pas pertinents : ils ne respectent pas les contraintes de l’architecture du FPGA utilisé (bande passante, ressources, mémoire, etc.) ou ne sont pas sur le front de Pareto (d’autres points ont des qualités supérieures dans toutes les dimensions). Les points pertinents sont multiples et offrent des compromis entre différents objectifs à optimiser (débit, latence, efficacité énergétique, puissance, énergie, précision ou qualité, etc.). Par exemple, le choix de la taille des mots offre un compromis entre qualité (due à la quantification des données) et performance et efficacité énergétique (dues aux opérateurs plus petits, aux mémoires plus petites, etc.).
Il faut cependant noter que les caractéristiques d’un point de l’espace de conception (comme les ressources utilisées, l’efficacité énergétique) ne peuvent pas toutes être connues facilement (sans créer l’architecture et effectuer tout ou partie du long processus de syn-thèse logique). Il est toutefois possible de les estimer en utilisant un modèle analytique afin de sélectionner des solutions proches du front de Pareto.
Intégration
Les FPGA peuvent être utilisés en autonomie (par exemple avec une interface réseau) ou conjointement avec un processeur hôte de la même manière que les GPU et les TPU. Dans ce dernier cas, le FPGA est utilisé par l’hôte comme accélérateur matériel pour les calculs les plus critiques. On peut retrouver le FPGA seul ou intégré avec les(s) proces-seur(s) sur un même système sur puce (System On a Chip, SOC), interconnectés par un bus mémoire. Par exemple, les FPGA haut de gamme de Xilinx (architecture Ultrascale) sont disponibles avec l’architecture Zynq (le FPGA et les six processeurs ARM sont inter-connectés par un bus Advanced Microcontroller Bus Architecture (AMBA)) ou seuls. On retrouve notamment le FPGA seul sur une carte destinée aux centres de données (Alveo U280) permettant de le connecter à un système hôte via un bus PCI Express, comme un GPU.

Le rapport de stage ou le pfe est un document d’analyse, de synthèse et d’évaluation de votre apprentissage, c’est pour cela chatpfe.com propose le téléchargement des modèles complet de projet de fin d’étude, rapport de stage, mémoire, pfe, thèse, pour connaître la méthodologie à avoir et savoir comment construire les parties d’un projet de fin d’étude.

Table des matières

Introduction
Contributions
Plan du document
1 Contexte 
1.1 Compromis performance/puissance
1.1.1 Puissance et performance d’un transistor
1.1.2 Gestion dynamique de la fréquence et de la tension
1.2 Impact de la microarchitecture des processeurs sur les performances et l’énergie
1.2.1 Parallélisme d’instruction
1.2.2 Ordonnancement dynamique
1.2.3 Exécution spéculative
1.2.4 Jeu d’instructions vectoriel
1.2.5 Hiérarchie mémoire
1.2.6 Limites
1.3 Architectures explicitement parallèles
1.3.1 Processeur à jeu d’instruction explicitement parallèle
1.3.2 Processeur graphique
1.3.3 Processeur spécialisé pour l’intelligence artificielle
1.3.4 Discussion
1.4 Architectures dédiées
1.4.1 Architecture des FPGA
1.4.2 Modèle de programmation
1.4.3 Exploration de l’espace de conception
1.4.4 Intégration
1.5 Conclusion
2 Spéculation temporelle pour FPGA 
2.1 Spéculation temporelle
2.1.1 Fréquence maximale d’un circuit
2.1.2 Overclocking et Undervolting
2.1.3 Variabilité
2.2 Tolérance aux fautes
2.2.1 Fautes, erreurs, pannes
2.2.2 Phénomènes physiques pouvant provoquer une faute
2.2.3 Erreurs temporelles
2.2.4 Comparaison des mécanismes de tolérance aux fautes existants
2.2.4.1 Redondance : duplication et triplication
2.2.4.2 Codes correcteurs d’erreur
2.2.4.3 Redondance basée sur l’arithmétique modulaire
2.2.4.4 Détection basée sur le double échantillonnage
2.2.4.5 Tolérance aux fautes logicielle
2.2.4.6 Tolérance aux fautes au niveau algorithmique
2.2.4.7 Synthèse
2.3 Conclusion
3 Réseaux de neurones convolutifs et FPGA
3.1 Réseaux de neurones convolutifs
3.1.1 Architecture
3.1.2 Types de couches
3.1.3 Optimisations algorithmiques
3.1.4 Représentation des données
3.1.5 Mises en œuvre sur FPGA
3.2 Spéculation temporelle pour accélérateurs de convolution
3.3 Impact des erreurs sur la précision
3.4 Détection d’erreur pour CNN
3.5 Conclusion
4 Spéculation temporelle sûre pour réseaux de neurones convolutifs 
4.1 ABFT pour les CNN
4.1.1 Vue d’ensemble
4.1.2 ABFT pour convolution 1D
4.1.3 ABFT pour convolution 2D
4.1.4 ABFT pour sommes de convolution 2D (CNN)
4.1.5 ABFT pour convolution avec pas non unitaire
4.1.6 Autres couches de CNN
4.2 Accélérateur de convolution tolérant aux fautes
4.2.1 Accélérateur de référence
4.2.2 Détection d’erreur pour un accélérateur de convolution
4.2.3 Mise en œuvre des calculs de sommes de contrôle
4.2.3.1 Somme de contrôle de sortie
4.2.3.2 Somme de contrôle d’entrée
4.2.4 Intégration complète
4.2.4.1 Gestion de la fréquence
4.2.4.2 Architectures en streaming et moteur de calcul unique
4.3 Évaluation
4.3.1 Gains en performance et pénalités dues aux erreurs
4.3.2 Correction de la détection d’erreur
4.3.3 Convolution convertie en multiplication de matrice
4.3.4 Plateforme expérimentale
4.3.5 Amélioration des performances
4.3.6 Amélioration de l’efficacité énergétique
4.3.7 Gains en performance, en efficacité énergétique et surcoût dû à la détection d’erreur
4.3.8 Erreurs observées
4.3.9 Faux négatifs
4.4 Discussion
4.4.1 Overclocking et undervolting
4.4.2 Représentation des données
4.4.3 Variations algorithmiques de la convolution
4.4.4 Applicabilité à d’autres architectures
4.4.5 Applicabilité à d’autres algorithmes et génération automatique
4.4.6 Limites
4.5 Conclusion
Conclusion et travaux futurs
Perspectives de recherche
Liste des publications
Bibliographie 

Télécharger le rapport complet

Télécharger aussi :

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *