Quelle énergie pour quelle vitesse avec quel langage de programmation ?

Publié le 14 août 2012 par Lbloch

Sommaire-

Le numéro de juillet 2012 des Communications of the ACM (CACM vol. 55 n° 7) comporte un article de Hadi Esmaeilzadeh, Ting Cao, Xi Yang, Stephen M. Blackburn et Kathryn S. McKinley, Looking Back and Looking Forward : Power, Performance, and Upheaval, qui renouvelle sensiblement la problématique de la mesure de performance des ordinateurs. En effet, la plupart des benchmarks disponibles aujourd'hui utilisent des programmes écrits il y a trente ans avec des langages de programmation de l'époque. Les auteurs de l'article montrent que les mesures effectuées avec des langages modernes, dotés de gestion automatique de mémoire et de capacités de parallélisme à grain fin (multi-threading), donnent des résultats bien différents.

Cet article est précédé d'une introduction de David A. Patterson, architecte des processeurs SPARC et auteur du fameux manuel Computer Architecture - A Quantitative Approach avec John L. Hennessy, l'architecte des processeurs MIPS, livre qui a renouvelé en son temps le sujet en lui donnant une véritable consistance scientifique.

Naissance des benchmarks d'ordinateurs

La mesure des performances des processeurs a plus de trente cinq ans d'histoire, mais c'est en 1977 que fut commercialisé un ordinateur réputé exécuter un million d'instructions par seconde (1 MIPS), le VAX 11/780 de Digital Equipment, qui pour longtemps fera office de mètre-étalon des ordinateurs. Afin de faire des mesures et des comparaisons, chercheurs et industriels élaborèrent des collections de logiciels représentatifs (des benchmarks) dont les temps d'exécution sur les ordinateurs observés permettaient de les placer sur des échelles de performance relative. Ainsi, Jack Dongarra de l'Université du Tennessee a mis au point dans les années 1970 une collection de programmes de calcul numérique pour l'algèbre linéaire, LINPACK, dont l'exécution permettait de comparer la vitesse de calcul des super-ordinateurs de l'époque. Nhuan Doduc de Framentec a publié en 1989 les temps d'exécution d'une série de programmes FORTRAN sur des centaines de machines. La société Standard Performance Evaluation Corporation (SPEC) publie depuis 1992 des suites de programmes destinées à évaluer les performances de calcul sur les entiers (SPECint) ou en virgule flottante (SPECfp). Tous ces logiciels furent écrits en FORTRAN et en C, avec quelques ajouts en C++ au fil du temps.

Il convient de préciser que de telles campagnes de mesures sont des opérations très délicates : il n'est pas toujours facile de savoir ce que l'on mesure exactement, et il est encore plus difficile de produire des résultats comparables pour des ordinateurs d'architectures différentes, et pour des programmes compilés avec des compilateurs différents. De nombreux artefacts peuvent intervenir pour perturber les résultats, notamment la taille des mémoires caches [1] : si nous comparons deux processeurs A et B identiques, sauf pour la taille de cache de premier niveau, un peu plus grande pour A que pour B, en leur soumettant un programme de calcul linéaire sur des tableaux tels qu'ils tiennent dans le cache de A et pas dans le cache de B, les résultats feront apparaître une supériorité écrasante pour A, alors que pour des tableaux de taille inférieure à la taille du cache de B les résultats seront identiques. L'art de l'auteur de benchmark consiste à repérer de tels artefacts et à les interpréter correctement. Il convient d'avoir à l'esprit les chiffres que tout programmeur doit connaître, ici des commentaires.

Parallèlement à ces observations empiriques, d'autres chercheurs élaboraient une réflexion plus englobante, notamment Hennessy et Patterson, cités plus haut, dont le livre Computer Architecture - A Quantitative Approach a vraiment représenté un tournant : ces deux auteurs associaient en effet une expérience pratique considérable de conception et de mise en production industrielle de processeurs réels à un parcours de recherche théorique dans le domaine.

Nouveaux problèmes, nouveaux langages

Il y a cinq ou six ans, les questions que cherchent à résoudre les concepteurs de processeurs ont subi un renversement radical. La course à l'accroissement de fréquence a cessé, parce que les avantages qu'elle pouvait procurer commencent à décroître (cf. Les microprocesseurs, leur histoire et leur avenir). La consommation électrique est devenue une préoccupation essentielle, parce que le prolongement des tendances actuelles donnerait 500 W par centimètre carré en 2018, ce qui est insoutenable. Pour utiliser judicieusement les quelques deux milliards de transistors que proposent sur deux ou trois centimètres carrés les technologies actuelles, les architectes d'aujourd'hui placent plusieurs processeurs sur le même chip (processeur multi-cœurs), et afin de mieux les utiliser ils développent les capacités de multiprogrammation fine (multithreading).

Cette évolution des architectures matérielles a des répercussions sur le logiciel, elle pousse à utiliser des langages de programmation mieux adaptés, par exemple les langages fonctionnels. La gestion explicite des allocations et désallocations de mémoire, outre ses conséquences désastreuses du point de vue de la sûreté des programmes, n'est plus compatible avec les environnements techniques actuels. Cette évolution donne des arguments en faveur de Java, un langage doté d'une gestion automatique de mémoire et de capacités de multithreading incorporées à la machine virtuelle, avec les avantages de portabilité qui en découlent.

Résultats pour 61 groupes de programmes et huit processeurs

Nos auteurs ont accompli un travail empirique considérable en mesurant les caractéristiques d'exécution de 61 benchmarks sur huit processeurs Intel de générations différentes : Pentium 4 (sorti en mai 2003), Core 2 Duo E6600, Core 2 Quad Q 6600, Core i7 920, Atom 230, Core 2 Duo E7600, Atom D510 et Core i5 670 (sorti en janvier 2010).

Les benchmarks utilisés se répartissent en quatre familles, deux pour les programmes de style ancien sans gestion automatique de mémoire, deux pour Java :

programmes C, C++ et Fortran non parallélisables et intensifs en calcul issus de la suite SPEC CPU 2006 ;
programmes C et C++ multithreaded de la suite PARSEC ;
programmes Java peu parallélisables issus de SPECjvm, DaCapo et pjbb2005 ;
programmes Java parallélisables issus de DaCapo 9.12.

Pour chaque benchmark, les auteurs ont mesuré les temps d'exécution, ce qui est habituel, mais aussi la consommation électrique, par une instrumentation des cartes mères, ce qui est moins banal. Voici les principales conclusions tirées de leurs observations, certaines sont très inattendues :

la puissance électrique dissipée dépend énormément du type de programme exécuté, et fort peu de la puissance électrique nominale du circuit ;
pour une micro-architecture donnée, la puissance électrique par transistor est à peu près constante et indépendante de la technologie de réalisation ;
si l'on compare les résultats d'un circuit bi-cœur à ceux du même circuit avec un cœur désactivé, on constate que l'activation des deux cœurs n'est pas toujours une source de gain en efficacité énergétique ;
la machine virtuelle Java (JVM) introduit du parallélisme même pour l'exécution de programmes à un seul fil d'exécution, parce que le glaneur de cellules (garbage collector), le compilateur just in time (JIT) et le profileur en tirent parti ;
le simultaneous multithreading (SMT) procure des économies d'énergie considérables aux processeurs les plus modernes et aux processeurs à exécution in order ;
les architectures out of order et les architectures in order ont des efficacités énergétiques similaires.

Sans surprise, on observe que les benchmarks Java tirent un meilleur parti du simultaneous multithreading et des cœurs multiples que les benchmarks basés sur les langages traditionnels.

Les auteurs insistent sur le fait que l'énergie électrique consommée pour l'exécution d'un programme donné est une grandeur plus significative que la puissance.

Cet article, absolument pionnier dans sa démarche et dans ses résultats, vient utilement compléter les résultats exposés dans deux articles précédents.


[1] Le mot cache, curieux retour au français d'un emprunt anglais, suggère ici l'idée de cacher dans un coin (techniquement parlant, dans une zone de mémoire petite mais à accès très rapide) pour l'avoir sous la main une chose que l'on ne veut pas avoir à aller chercher à la cave ou au grenier (i.e., dans la mémoire centrale, vaste mais à accès lent par rapport à la vitesse du processeur), pour gagner du temps. Cette technique s'est développée parce que, si la capacité de la mémoire centrale a évolué parallèlement à la puissance des processeurs, sa vitesse a progressé moins vite. Si au début des années 1990 le temps d'accès à une position de mémoire se mesurait en dizaines de cycles de processeurs, vingt ans plus tard c'est en centaines de cycles : il a fallu trouver des procédés pour éviter que cette discordance croissante ne réduise à néant les bénéfices obtenus pas l'accélération des processeurs.

La technique du cache procure des augmentations de performances très spectaculaires, bien qu'aucun modèle général satisfaisant de son fonctionnement n'ait pu être proposé à ce jour. Une variante en est le TLB (Translation Lookaside Buffer), qui conserve « sous la main » les résultats les plus récents de traductions d'adresses virtuelles en adresses réelles, ce qui permet avec un très faible volume de données d'obtenir des accélérations spectaculaires.