Site WWW de Laurent Bloch
Slogan du site

ISSN 2271-3905
Cliquez ici si vous voulez visiter mon autre site, orienté vers des sujets moins techniques.

Pour recevoir (au plus une fois par semaine) les nouveautés de ce site, indiquez ici votre adresse électronique :

Révolution dans les architectures de calcul, avec les circuits logiques programmables
Article mis en ligne le 4 février 2014
dernière modification le 5 février 2014

par Laurent Bloch

Les architectures de processeurs actuelles approchent de leurs limites

Après quinze ans de stabilité architecturale (autour du modèle Intel x86 et des cartes-mères issues des premiers PC) assurée par la croissance uniforme des performances des composants selon la loi de Moore, le matériel informatique entre dans une période de turbulences et d’innovations comparable à l’époque 1980 - 2000, entre la fin de l’hégémonie d’IBM et le début de celle d’Intel et de Microsoft [1].

La raison en est que la technologie actuelle, qui a assuré ce progrès continu pendant quarante ans, unique dans l’histoire des techniques, atteint des limites, qui commencent à éroder les gains de performances obtenus par les procédés classiques de miniaturisation des composants. Lorsque l’épaisseur de la grille d’un transistor se compte en atomes, on comprend que l’on ne pourra plus aller très loin dans cette direction. Lorsque la prolongation des tendances actuelles débouche sur une dissipation calorique de 500W par cm2 en 2018, c’est-à-dire proche de ce qui se passe au cœur d’un réacteur nucléaire, on doit s’attendre à quelques difficultés. Mais le premier obstacle aujourd’hui, c’est que la taille du motif de base du dessin des composants est inférieure à la longueur d’onde de l’ultraviolet extrême. Comme ces circuits sont réalisés par photolithographie, et que les rayons X qui viennent ensuite n’ont pas les propriétés adéquates, il va falloir trouver autre chose.

Si accroître la performance unitaire des processeurs devient de plus en plus difficile, une solution peut consister à en associer plusieurs pour faire le même travail, et c’est ce qui se passe depuis quelques années avec les processeurs multi-cœurs et les logiciels multi-threads (désolé, pas de traduction française utilisable). Malheureusement, la parallélisation d’algorithme est un exercice non trivial, cela peut demander des années de travail, pour un résultat parfois décevant, et il y a des cas pour lesquels c’est tout simplement impossible. Historiquement, les recherches en parallélisme n’ont pas débouché sur des succès industriels, parce que pendant le temps mis à adapter les logiciels, les performances unitaires des processeurs avaient comblé l’écart avec les algorithmes parallèles. Mais cette situation peut changer, c’est même très probable.

On peut aussi utiliser des architectures de processeur plus efficaces ; x86 est loin d’être un optimum, il repose sur le modèle d’architecture CISC (Complex instruction set computer), considérée aujourd’hui comme lourde et inefficace par rapport à l’architecture RISC (Reduced Instruction Set Computer). Intel a réussi à imposer son modèle pour le même genre de raisons que celles qui ont assuré le succès du moteur à pistons, bielles et vilebrequin face à des solutions plus efficaces : la base installée constituait un redoutable volant d’inertie, et les revenus qu’elle générait permettait les investissements énormes qui en réduisaient les faiblesses. Mais aujourd’hui on touche au bout de ce processus, et le RISC revient, sous les couleurs du concepteur britannique ARM, dont les circuits ont commencé par équiper la quasi-totalité des smartphones et des tablettes, avant de s’attaquer au marché des serveurs. Les processeurs RISC Cortex A57 d’ARM, comparés à des Intel ou des AMD x86 de puissance de calcul comparable, devraient avoir une consommation électrique et une surface divisées par 4 selon les estimations. C’est bien, mais ne fait que reculer l’échéance.

Y a-t-il d’autres voies ?

Circuits logiques programmables : une informatique toute autre !

Le numéro de décembre 2013 des Communications of the ACM [2] consacre son dossier Research Highlights à ce qu’en français on appelle « glaner des cellules » (pour avoir les mêmes initiales GC que l’anglais Garbage collector), sous le titre intrigant de And Then There Were None : A Stall-Free Real-Time Garbage Collector for Reconfigurable Hardware. L’article principal [3] est signé David F. Bacon, Perry Cheng et Sunil Shukla du Centre de recherche T.J. Watson d’IBM, introduit par une présentation très utile d’Eliot Moss, de l’université du Massachusetts à Amherst.

*Glaner des cellules

Le GC (glaneur de cellules, donc) est un dispositif dont tout langage de programmation de haut niveau (par opposition à « proche du matériel », comme C) moderne doit être équipé. En effet, un logiciel de notre époque, tel qu’un navigateur Web ou un système de gestion de contenus adossé à une base de données, passe son temps à créer des objets, pour lesquels il alloue dynamiquement de l’espace mémoire, et lorsque ces objets ont cessé d’être utiles, il faut libérer cet espace, faute de quoi le logiciel est exposé à une fuite de mémoire, pathologie grave et potentiellement mortelle.

La gestion par le programmeur des allocations et désallocations d’espace mémoire est un exercice délicat, source d’innombrables erreurs, et même le programmeur compétent n’est jamais à l’abri d’une négligence aux conséquences potentiellement graves. Le GC est là pour se substituer au programmeur faillible : dans les langages à gestion de mémoire automatique (Lisp, Scheme, OCaml, Java, PHP, JavaScript, Ruby, SmallTalk...) la création d’objet comporte l’allocation de l’espace mémoire nécessaire, sans nécessité de faire appel à un quelconque malloc ou son équivalent. On pourrait se dire qu’ensuite il suffit de libérer l’espace quand l’objet n’est plus utilisé, mais ce n’est pas si simple : le processus ou le thread qui a créé l’objet peut en avoir donné la référence à un autre processus ou thread, et même si le processus créateur se termine, un autre peut toujours avoir besoin d’accéder à l’objet. Déterminer qu’un objet n’est plus et ne sera plus utilisé, et qu’il peut donc être détruit, est une opération complexe ; c’est le travail du GC. Pour le programmeur qui utilise un tel langage c’est très pratique, il suffit de ne s’occuper de rien. Pour le concepteur de langage c’est une autre paire de manches.

Depuis l’origine des temps (John McCarthy pour Lisp en 1960) la conception de GC a défié l’ingéniosité des programmeurs. La plupart des solutions reposent sur la même idée de départ : partir d’une liste d’objets en mémoire dont on sait (enfin, dont on a de bonnes raisons de penser) qu’ils sont utilisés (ce sont les racines du GC, par exemple les registres, le pointeur d’instruction, la pile...), identifier les objets auxquels ils font référence, et ainsi de suite récursivement ; on a ainsi marqué les objets potentiellement utilisés. Les objets qui ne sont pas dans cette liste ne pourront être utilisés par aucun programme, puisqu’aucune référence ne permet d’y accéder : ils peuvent donc être détruits. D’autres algorithmes entretiennent pour chaque objet un compteur de références, lorsque la valeur du compteur atteint zéro, l’objet peut être détruit.

Cela posé, plusieurs stratégies sont envisageables :

 attendre sans rien faire que la mémoire soit presque saturée, alors tout arrêter (Stop the World) et glaner ; l’inconvénient est alors de provoquer une pause du système qui peut durer un certain temps, ce qui est par exemple inacceptable pour le logiciel de pilotage d’un avion ou d’un train ;
 faire marcher le GC parallèlement aux autres logiciels, en veillant à leur donner des priorités telles que le GC vide la mémoire plus vite que les autres ne la remplissent, mais que néanmoins il n’absorbe pas toute la puissance de calcul ;
 d’autres encore, le sujet est quasi inépuisable, puisque sans solution parfaite ; une excellente référence sur le sujet est le chapitre qu’y consacre le livre d’Abelson et Sussmann Structure and Interpretation of Computer Programs.

Alors, qu’est-ce que cela peut bien avoir à faire avec le matériel ? C’est là que la chose devient intrigante. David F. Bacon, Perry Cheng et Sunil Shukla ont imaginé un GC en matériel, et en plus c’est plus simple et plus puissant qu’en logiciel. Voyons cela.

*Programmation des FPGA

Les circuits logiques programmables (Field-programmable Gate Arrays, FPGA) sont des composants constitués d’un grand nombre de cellules dont chacune contient le circuit d’une fonction logique élémentaire. Ces objets sont programmables en deux sens :

 on peut activer ou désactiver les cellules individuelles, et les connecter entre elles de façon arbitraire ;
 une fois que l’on a ainsi construit un circuit selon l’objectif poursuivi, les données qui lui sont fournies en entrée déterminent son comportement.

Une autre caractéristique intéressante de ces circuits, c’est que chaque cellule peut être munie d’une mémoire locale. Alors que les premiers FPGA offraient des fonctions assez rudimentaires, les modèles récents profitent de la loi de Moore pour offrir des fonction plus complexes, ainsi que des vitesses d’exécution et des capacités de mémoire respectables.

Pendant longtemps, parmi les domaines de prédilection des FPGA étaient les data planes (ou forwarding planes) des routeurs (il s’agit de faire transiter vite des paquets vers la bonne interface, en fonction de règles qui se plient bien à la logique booléenne) et les pare-feu (pour appliquer sans délai des règles là aussi assez booléennes : je transmets ou je jette). Les composants actuels permettent d’avoir dans chaque cellule des fonctions plus complexes, munies d’une mémoire locale conséquente, ce qui autorise une vraie programmation répartie entre des cellules spécialisées, ce qui est une véritable transgression du modèle de von Neumann, par opposition au pipe-line des processeurs classiques et au multi-threading, qui permettent d’accélérer von Neumann sans modifier la sémantique des traitements.

Les auteurs de notre article ont écrit un GC pour un FPGA, ce qui semble une gageure, et pourtant cela marche très bien (on pourra lire ici une version préliminaire disponible en ligne de leur article, ainsi qu’une suite). Ils ont imposé un modèle simple pour la représentation des objets en mémoire, et tiré parti du fait que sur ce type de circuit la mémoire est accessible par deux ports simultanément, ce qui permet une vraie simultanéité de deux accès à la mémoire.

La teneur des articles est assez technique et je n’irai pas plus loin dans leur résumé, mais il s’agit clairement d’une contribution très intéressante à l’élaboration de l’informatique d’après la loi de Moore et d’après von Neumann [4]. Ce n’est pas rien.