Les compilateurs sont des programmes informatiques d�une importante complexit�. Ils sont g�n�ralement structur�s en une partie frontale, qui comprend le langage d�entr�e, puis d�une partie arri�re, qui s�occupe de traduire le programme d�entr�e en du code binaire ex�cutable par le processeur. Les deux parties communiquent � l�aide d�une repr�sentation interm�diaire (IR). Ainsi, pour qu�une m�me suite de compilateurs comprenne plusieurs langages et puisse g�n�rer du code pour plusieurs processeurs, il n�est pas n�cessaire d��crire un compilateur sp�cifique pour chaque paire langage-processeur : il suffit d��crire les paires langage-IR et IR-processeur. Tout le travail effectu� pour un processeur sera alors utilisable pour n�importe quel langage d�entr�e.
D�s la g�n�ration du code interm�diaire, un compilateur utilise �norm�ment de passes d�optimisation, afin de rendre le code plus rapide. Elle utilise des techniques comme la propagation des valeurs : si une variable a une valeur connue � un endroit du code (initialisation, condition�), alors cette valeur peut �tre propag�e dans une s�rie de calculs, qui seront alors effectu�s � la compilation au lieu de l�ex�cution. Ces passes sont effectu�es en partie sur la repr�sentation interm�diaire, pour les optimisations ind�pendantes du processeur, mais �galement sur du code machine d�j� g�n�r�, pour tirer le meilleur parti possible du mat�riel.
G�n�ralement, cette phase d�optimisation est extr�mement cruciale, �tant donn� que la repr�sentation interm�diaire est suffisamment g�n�rale pour plusieurs types de processeurs et relativement simple dans les instructions disponibles : pour effectuer certaines op�rations, il est parfois n�cessaire d��crire des dizaines d�instructions IR, alors que certains processeurs peuvent r�aliser la m�me op�ration en une seule instruction. De plus, une repr�sentation interm�diaire doit �tre pr�te pour les phases d�optimisation qui suivent, notamment en simplifiant toutes les �tapes de raisonnement : dans les IR de type SSA, notamment, une variable ne peut �tre assign�e qu�une seule fois, ce qui allonge d�autant plus le code.
Un nouvel optimiseur pour Visual C++
Le compilateur C++ de Microsoft, connu sous le nom de Visual C++, a longtemps �t� laiss� dans un �tat proche de l�abandon. Depuis quelques ann�es, les �quipes de d�veloppement ont mis les bouch�es doubles pour ramener le compilateur dans la course, avec une compatibilit� avec les normes les plus r�centes, au niveau de GCC ou Clang. Depuis lors, ils ont effectivement rattrap� en grande partie leur retard � ce niveau, mais pas encore pour les optimisations du code.
L�optimiseur actuel ne disposait que d�une s�rie de transformations assez basiques, n�exploitant qu�une vue assez limit�e des fonctions. Bon nombre d�optimisations fonctionnent en trouvant des motifs et en les rempla�ant par une version am�lior�e, mais tous les motifs les plus utiles ne sont pas toujours impl�ment�s. De plus, le code vectorisable n�est pas optimis� au meilleur de ses possibilit�s. Toutes ces limitations ont une origine historique : le compilateur C++ de Microsoft a des origines tr�s anciennes, il provient de l��poque DOS o� la m�moire �tait tr�s limit�e, ce qui limite les possibilit�s. Les efforts actuels portent principalement sur une modernisation du code, en �liminant les compromis d�un autre �ge.
Le nouvel optimiseur exploite d�sormais une repr�sentation interm�diaire SSA. Les algorithmes impl�ment�s par-dessus peuvent ainsi �tre plus efficaces en temps par rapport aux approches par analyse du flux de donn�es. Il se base sur une biblioth�que C++ avanc�e, exploitant les templates � foison, pour d�crire les transformations � effectuer, ce qui a permis d�ajouter bon nombre d�optimisations simples en tr�s peu de temps (surtout par rapport � l�infrastructure pr�c�dente).
Les d�veloppeurs de Visual C++ indiquent �galement avoir pr�t� attention � la correction de ces optimisations : il n�y a rien de plus d�sagr�able que de passer des journ�es � d�boguer son code pour trouver que, finalement, le probl�me n�est pas d� au code, mais bien au compilateur. Linus Torvalds a r�cemment torpill� GCC � ce sujet. Ici, les d�veloppeurs ont absolument cherch� � �viter ces �cueils, en testant leurs passes d�optimisation sur des programmes courants comme Chrome ou Firefox, puis sur des biblioth�ques imposantes c�t� Microsoft comme CoreCLR ou Chakra.
Ils ont aussi employ� des techniques de v�rification formelle (� l�aide d�ALIVE et l�outil de d�monstration automatique Z3, tous deux issus de Microsoft Research et aussi utilis�s pour LLVM) et de la g�n�ration de code al�atoire avec Csmith (et C-Reduce pour r�duire le code posant probl�me au strict minimum). Certains outils du projet LLVM ont pu �tre utilis�s, puisque Visual C++ peut en exploiter les parties avant, comme Opt-fuzz, qui g�n�re des expressions arithm�tiques � optimiser.
Un exemple
Ces nouvelles optimisations peuvent avoir un effet assez radical sur certains bouts de code. Par exemple, pour un test de parit� :
Code cpp : S�lectionner tout - Visualiser dans une fen�tre � part
1
2
3 int test(int a) { return a % 2 != 0 ? 4 : 2; }
Les pr�c�dentes versions du compilateur produisaient un code qui prenait entre cinq et dix cycles en x86, selon que tout le processeur est exploit� (pr�diction du branchement parfaite) ou non :
Code asm : S�lectionner tout - Visualiser dans une fen�tre � part
1
2
3
4
5
6
7
8
9
10
11
12 ?test@@YAHH@Z PROC and ecx, -2147483647 jge SHORT $LN3@test dec ecx or ecx, -2 inc ecx $LN3@test: test ecx, ecx mov eax, 2 mov edx, 4 cmovne eax, edx ret 0
Sauf que� Dans le test a % 2 == 0, le signe de a n�a aucune esp�ce d�importance, seul un bit est important : le modulo peut �tre remplac� par une op�ration logique, c�est-�-dire a & 1 == 0. Or, la comparaison implique un branchement dans le code, forc�ment lent sur les architectures x86 modernes : pour s�en d�barrasser, la structure bool(a) ? C1 : C2 peut �tre remplac�e par C2 + a*(C1-C2), c�est-�-dire exclusivement des calculs. Par cons�quent, le nouveau code assembleur est le suivant :
Code asm : S�lectionner tout - Visualiser dans une fen�tre � part
1
2
3
4 ?test@@YAHH@Z PROC and ecx, 1 lea eax, DWORD PTR [rcx*2+2] ret 0
Celui-ci prend, � tous les coups, deux cycles d�horloge : par rapport � cinq � dix cycles, le gain est important, tant en rapidit� qu�en taille de l�ex�cutable.
D�autres m�canismes effectuent une analyse statique du code, en pr�calculant autant de bits que possible dans les variables. Par exemple, lors d�une conversion vers un type plus grand, une s�rie de bits est forc�ment � z�ro, peu importe la valeur d�entr�e. Ensuite, cette information peut �tre adapt�e en fonction des op�rations effectu�es, ce qui peut guider le reste du processus.
Code cpp : S�lectionner tout - Visualiser dans une fen�tre � part
1
2
3
4
5
6 int test(unsigned char a) { short b = a; // b: 00000000________, a: ________ b <<= 4; // b: 0000________0000 b |= 3; // b: 0000________0011 return b != 0; // -> return true }
Impact sur la taille du code g�n�r�
L�impact de ces modifications sur le code g�n�r� n�est pas facile � �tablir : certaines optimisations diminuent forc�ment la quantit� de code, le compilateur sera alors plus enclin � recopier in extenso le code de petites fonctions pour �viter le surco�t d� � tout appel de fonction (qui n�est n�gligeable que pour des fonctions plus grandes) � ce qui conduit � l�augmentation de la taille du code. Il n�emp�che, les r�sultats sont int�ressants : sur tout Windows, c�est-�-dire plus d�un gigaoctet, les gains sont de 438 Ko par rapport � l�optimiseur pr�c�dent (0,3 %) ; sur SQL Server, 46 Ko sur 64 Mo (0,8 %) ; sur Chakra, le nouveau moteur JavaScript, 10 Ko sur 6 Mo (1,7 %).
En analysant plus en d�tail l�impact au niveau des instructions, les plus lentes sont largement �vit�es (branchements, multiplications, divisions), remplac�es par des op�rations plus rapides (comme des d�placements conditionnels). Les temps de compilation ont �t� impact� de diverses mani�res : une augmentation de 1,7 % pour Chrome ou une diminution de 2,6 % pour le noyau Windows.
Et alors ?
Les travaux sur l�optimiseur viennent seulement d��tre annonc�s : l�architecture choisie permettra aux d�veloppeurs d�ajouter des optimisations suppl�mentaires rapidement (et certaines sont d�j� en cours de planification). Cet optimiseur devrait arriver d�s Visual C++ 2015 Update 3, mais le travail continuera sur cet aspect du compilateur, afin de le rendre comp�titif par rapport � ses concurrents, voire de les d�passer. Il est d�ores et d�j� possible de le tester.
Source : Introducing a new, advanced Visual C++ code optimizer.
L�image a �t� cr��e par l�auteur et est soumise au droit d�auteur.
Ce contenu a �t� publi� dans C++ par dourouc05.
Partager