Y a-t-il des langages plus verts que d'autres?

La vie d'une application informatique (expression de besoins, spécifications, cycle développement/qualification, production, fin d'exploitation) est jalonnée de prises de décision impactant fortement son empreinte environnementale globale.

En amont de la phase de développement, le choix d'un langage de programmation fait-il partie de ces décisions cruciales ? En particulier, ce choix a t-il des conséquences quantifiables sur la consommation énergétique d'un logiciel ?

Nous allons répondre à cette dernière question dans le cadre restreint d'une étude de cas. Nous y comparons les consommations électriques induites par l'exécution d'une série de 4 programmes à la fonctionnalité identique écrits en C++ et par l'exécution des mêmes programmes rédigés en Cawen, le langage que nous développons actuellement.

 

 

Le banc d'essai

Parmi les tests comparatifs inter-langages disponibles sur la toile, nous avons choisi d'utiliser celui de Robert Hundt, un ingénieur R&D de Google.

Son étude, publiée en 2011, a consisté à implémenter le même algorithme de recherche opérationnelle (détection de boucles dans un graphe, d'après les travaux de P.Havlak et R.E. Tarjan) dans 4 langages différents : C++, Java, go et Scala.

Pour chacun de ces langages, il a relevé la longueur du code source, le temps de compilation et la taille des exécutables générés.

A l'exécution, il a également mesuré la mémoire consommée ainsi que le temps de traitement.

Les résultats sont disponibles ici.

 

Les premiers résultats

En ce qui concerne les critères liés à l'exécution, C++ l'emporte largement sur ses concurrents : l'exécutable issu de la compilation C++ consomme entre 3 et 4 fois moins de mémoire et achève sont traitement 2,5 fois à 12,5 fois plus vite que les autres participants.

C++ ayant également obtenu de bons résultats sur les critères statiques, il a été très légitimement déclaré vainqueur de l'épreuve.

 

Optimisations et variantes

Robert Hundt a confié le problème à quelques spécialistes du C++ qui, en remplaçant certains objets containers, on aboutit à une version optimisée de son programme initial.

Indépendamment, deux autres développeurs, Russel Cox (promoteur de go, il explique sa procédure d'optimisation ici) et Anthony C. Hay, ont proposé leur propre implémentation.

 

Performance et consommation électrique

Nous avons utilisé le logiciel de mesure et suivi la méthodologie du Green Code Lab et, guidés par les conseils éclairants de ses contributeurs, nous avons mesuré la consommation électrique que nécessite l'exécution de ces différentes versions de la maquette.

Notre contexte de test est le suivant :

gcc 4.5.3 / Cygwin6.0/ Intel Pentium Dual-Core T4200 /64b/2GHz /4G RAM .

C'est la configuration que nous nommons cygwin sur notre site.

En superposant les courbes de consommation on obtient :

Et voici le classement d'étape :

R.Cox, A.Hay, R.Hundt 2 puis R.Hundt 1.

Il recoupe le classement des temps d'exécution.

Entre la plus sobre et la plus gourmande des implémentations, le rapport énergétique est de 1 à 21.

On peut en tirer une conclusion intermédiaire qui n'étonnera personne:

A fonctionnalité égale, il peut exister un écart de performance énorme entre deux implémentations réalisées dans le même langage.

Les gains de performances sont obtenus essentiellement par remplacement de certaines listes et tables de hachage par des tableaux.

 

Et si on modifiait les données d'entrée?

Assez curieusement, le testeur de Robert Hundt exécute 15000 fois le même traitement sur un unique graphe arbitraire.

Anthony Hay s'en est étonné et a proposé de soumettre aux différents exécutables des populations de graphes générés aléatoirement.

Nous avons ainsi constitué un jeu de 100 graphes aléatoires d'au plus 10000 sommets et de 100 graphes aléatoires constitués d'au plus 50000 sommets.

Le classement est bouleversé.

Il devient, pour les graphes d'au plus 10000 sommets : R.Hundt 2, A.Hay, R.Cox, R.Hundt1

Entre la plus sobre et la plus gourmande des implémentations, le rapport énergétique est de 1 à 4,1.

Et pour les graphes d'au plus 50000 sommets : R.Hundt 2, R.Hundt 1, A.Hay, R.Cox

Entre la plus sobre et la plus gourmande des implémentations, le rapport énergétique est de 1 à 2,3.

C'est précisément ce qui a fait l'avantage des versions Cox et Hay dans le test précédent qui les pénalise maintenant : pour un grand nombre de valeurs, la recherche dans un tableau est beaucoup plus lente que dans une table de hachage.

Avec ces nouvelles entrées, les coûts d'insertion/deletion dans les tables de hachage de R.Hundt 1 & 2 sont complétement amortis par l'accélération obtenue sur la primitive de recherche.

On peut remarquer également que les optimisations apportées à la version R. Hundt 1 deviennent inopérantes sur les graphes d'au plus 50000 sommets : pour cet échantillonnage, R. Hundt 1 et R.Hundt 2 sont au coude à coude.

Un informaticien désirant réduire la consommation électrique de son projet de «recherche de boucle dans des graphes» (un marché de niche s'il en est!;-) choisira la solution de Hundt s'il doit traiter des graphes similaires au graphe initial et les solutions de Cox ou Hay si les graphes d'entrée les plus probables ressemblent à nos séries aléatoires.

Les performances d'une application sont intimement liées à ses conditions d'utilisation et notamment, au volume et aux valeurs des données d'entrée. Parler d'optimisation d'une application sans connaissance des conditions de production n'a pas de sens.

 

Et si on changeait de langage ?

Faute de temps, nous n'avons pu mesurer la consommation électrique associée aux versions Java, Scala et go. Une mission pour un Green-code laborantin motivé?

Nous avons repris les versions C++ présentées plus haut et les avons traduites en Cawen, le langage que nous développons actuellement.

Nous avons testé les versions C++ & Cawen sur 5 machines (5 versions de gcc/ 4 OS)

Les résultats ainsi que les sources C++, Cawen et C sont disponibles ici.

Pour résumer, consommation mémoire, temps d'exécution, taille du source sont globalement et significativement meilleurs que ceux du C++ et, version après version, continuent de progresser depuis l'instantané pris lors de ces premières mesures.

Les temps de précompilation, très importants pour l'instant, seront ramenés à un niveau acceptable à très court terme (quelques semaines, quelques mois?).

Nous avons également rédigé des versions optimisées du code Cawen initial. Les gains de performances ont été réalisées par diverses méthodes adaptées au profil particulier de chaque programme.

Par rapport aux tests que nous présentons sur site, une différence qui a son importance: pour les mesures de consommation électrique nous avons décidé d'exploiter, à une échelle très modeste, le paramétrage du compilateur.

 

Paramétrage du compilateur

Une fois le code optimisé, il est possible de jouer avec les options du compilateur pour gagner encore en temps d'exécution, et donc, dans une certaine mesure, en consommation énergétique.

  • Paramètre d'optimisation générale

Robert Hundt utilise l'optimisation gcc O2 et non pas O3 pour la compilation de sa version.

C'est effectivement le bon choix sur sa machine de test, et par exemple sur notre serveur freebsd (12,4s contre 13,4s). Mais c'est pénalisant sur notre cygwin...Nous avons utilisé O3 pour tous les tests présentés ici.

  • Choix entre gcc et g++

Une particularité du code généré par le pré-compilateur Cawen est qu'il est compatible C99/C++. Passer d'un compilateur à l'autre peut s'avérer payant : pour random 10000 anthay, nous passons de 4,9s avec g++ à 2,8s avec gcc.

  • Déroulement des boucles

Notre première série de tests nous a indiqué que les algorithmes de A.Hay et R.Cox dépensaient le plus clair de leur temps dans une boucle de recherche parmi les valeurs d'un tableau.

Nous avons mis en place une réponse logicielle (via un templatage de la fonction de recherche) qui a permis de dérouler la boucle en question et d'améliorer sensiblement les performances de ces deux exécutables...Avant de nous apercevoir que gcc offrait le paramètre –funroll-loops pour effectuer exactement le même travail de façon transparente pour le développeur, lors de la compilation.

Par exemple, sur le test R. Cox/ random 10000, nous obtenons 13,2 sec sans le paramètre -funroll-loops et 5 sec avec....

Et avec ce flag, notre version optimisée de façon logicielle (voir la fonction govel_contains2 dans le fichier govel_typed.h) est pénalisée : les deux optimisations (paramètre –funroll-loops et déroulement logiciel) ne font pas bon ménage...

A vous de retrouver cette anomalie dans les graphiques qui suivent !

  • Paramétrage spécifique à la machine

La plupart du temps, les gains accessibles via le paramétrage de la compilation sont liés

à la plateforme (OS/processeur) et ne sont donc pas exportables.

Nous n'avons pas creusé les possibilités de gcc. Elles pourraient à elles seules faire l'objet de plus d'un article...

Mais il y a clairement là des réserves très importantes d'accélération. Par exemple, nous n'avons pas mis à contribution les primitives SSE permettant d'exploiter au mieux les architectures multi-core.

Sont-elles toujours traduisibles en gain énergétique ? Cela reste à prouver.

 

Que devient la consommation électrique?

Voici les mesures pour chaque version codées en C++, en Cawen (traduction littérale de la version C++) et Cawen optimisé (version optimisée de la première version Cawen).

Deux façons d'analyser ces résultats : on peut considérer qu'on a créé 4 programmes indépendants dans deux langages différents et, pour chacune des trois utilisations envisagées (bench, random 10000, random 50000) on obtient les économies énergétiques suivantes :

On peut également considérer que le problème était, pour chaque type d'utilisations, de déterminer l'exécutable consommant le moins d'énergie possible. 

Dans cette optique, en mode «bench», le meilleur programme Cawen (cawen rcox) consomme 15 % de moins que le meilleur programme C++ (cpp rcox), le meilleur programme Cawen optimisé, 30 %.

En mode «graphes d'au plus 10000» sommets, le meilleur programme Cawen consomme 11 fois moins que le meilleur programme C++.

Et le meilleur programme Cawen optimisé consomme 17,7 fois moins que le meilleur programme C++.

En mode «graphes d'au plus 50000» sommets, le meilleur programme Cawen consomme 73 fois moins que le meilleur programme C++.

Et le meilleur programme Cawen optimisé consomme 243 fois moins que le meilleur programme C++.

 

Conclusion

Le lien entre les performances d'un programme et sa consommation électrique induite peut s'avérer complexe.

Dans le cadre de cette présentation amélioration des performances et économie d'énergie vont de pair.

Une démarche d'optimisation est d'autant plus fructueuse...

  • que les conditions d'utilisation du logiciel sont mieux connues et simulées

  • que les subtilités des librairies standards sont maîtrisés (pourquoi faut-il faire un resize sur certains tableaux et laisser certains autres grandir et rétrécir suivant les besoins ?)

  • que les algorithmes sous-jacents utilisés par ces dernières sont mieux compris (le choix table de hachage/ tableau)

  • que le schéma général de l'utilisation de la mémoire par l'application est mieux analysé (pourquoi l'utilisation d'un allocateur qui favorise tel programme pénalisera tel autre?)

  • que les possibilités du compilateur sont mieux exploitées.

Pour répondre à notre intitulé, le choix d'un langage, quand, par chance, il n'est pas imposé par le contexte fonctionnel, le profil des développeurs disponibles, ou pire, par la mode, a bel et bien un rôle primordial à jouer dans une démarche d'éco-conception logicielle.

C'est d'ailleurs ce qu'a pu expérimenter Facebook à grande échelle. Son exemple est devenu un classique de l'argumentaire éco-TIC : en 2010, l'entreprise a réduit de moitié sa facture d'électricité en passant du php à C++.

 

Et s'ils avaient utilisé Cawen?

 

Thomas Samain & Gwenaël Chailleu, 12/12/2012

Technologie: 
Catégorie: 

Ajouter un commentaire