Utiliser Intel VTune pour identifier les goulets d'étranglements

C'est le premier concours Accelerate sur lequel nous avons aussi facilement accès aux outils d'Intel tel qu'Intel Inspector, ou Intel VTune.
Lors de l'édition précédente, Maxime et moi avions souffert de ce manque. Nous avions identifié trop tard un goulet dans notre code en nous connectant en mode bureau sur la MTL par SSH pour exécuter Vtune et en regardant le profil d'exécution.

Cette fois ci, Intel a publié une version non commerciale de Intel Parallel Studio. J'ai trouvé l'ensemble facile à installer. J'ai beaucoup apprécié d'avoir accès facilement à ces outils.

Xavier Hallade de Intel a publié un billet expliquant comment créer un live CD contenant Intel parallel Studio.

J'écris ce billet pour décrire l'une de nos expériences lors du dernier concours.

A quoi sert Intel VTune ?



Il s'agit d'un profiler. L'outil est capable de vous indiquer le temps passé fonction par fonction (voir ligne par ligne) même dans un environnement multi threadé. Vous trouvez aussi dans le rapport un graphique montrant selon le profil sélectionné, les threads actifs, la charge CPU ... en fonction du temps.

Je vais revenir sur le profilage et la liaison avec le code source.

Matthias, l'un des collègues de ma promo, qui participait pour la première fois m'a contacté début mai. Il souhaitait savoir comment configurer VTune pour le lier au code source. Sur le coup, je me suis senti stupide. Je n'avais pas envisagé une seconde qu'il etait possible de faire ça.

En regardant le logiciel de plus prêt, le lien se fait tout naturellement dans les propriétés du projet. Une seule subtilité, vous devez compiler votre code en générant les symboles (avec l'attribut -g dans gcc/g++).

FLAGS ?= -std=c++0x -O3 -Wall $(GCC_SUPPFLAGS) -fopenmp -g

Fenetre propriétés du projet de Intel VTune


Notre cas :



Dans notre programme, nous utilisions une fonction de calcul qui permettait de calculer à partir d'un tableau de caractères une clef numérique.

Voici notre code d'origine :

KEY ReferenceSourceHash::getKey(char* pattern, int size) 
{
KEY key = 0;
for(int i = 0; i < this -> limit_key && i < size; i++)
{
switch(pattern[i])
{
case 'A':
break;
case 'C':
key += 1;
break;
case 'G':
key += 2;
break;
case 'T':
key += 3;
break;
}

key = key << 2;
}

key = key >> 2;
return key;
}





Vous pouvez remarquer le switch case dans ce code. Nous étions conscients de la faiblesse qu'il représentait. En effet, une instruction conditionnelle vient casser le flux d'exécution. Cependant, nous n'avons pas envisagé de le supprimer car nous ne pensions pas qu'il représentait un goulet d'étranglement. VTune ne taggait pas cette fonction et profilait à un niveau supérieur. Nous pensions que le goulet venait d'une autre de nos méthodes.

Voici le profil d'exécution que nous venions d'obtenir sur le scénario en utilisant 8 threads : /fr-fr/forums/showthread.php.

 Vue générale




Voici le profil d'exécution rapporté au code source :

 Vue code source



Vous remarquez que si nous optimisons cette fonction, nous pouvons obtenir un gain significatif. Une fois le problème identifié, il suffisait de quelques minutes pour écrire une solution bien plus efficace.

A présent, optimisons cette méthode :



Il existe une technique très efficace pour remplacer un switch / case de cette forme. Vous pouvez faire appel à une table de correspondance.

Par exemple :

    • Tab['A'] renvoie 0

    • Tab['C'] renvoie 1

  • ...



C'est une méthode très efficace qui a l'avantage de ne plus du tout faire appel aux instructions conditionnelles donc de fluidifier l'exécution.

Voici notre nouveau code :

KEY ReferenceSourceHash::getKey(char* pattern, int size) 
{
KEY key = 0;
int limit = this -> limit_key < size ? this -> limit_key : size;
for(int i = 0; i < limit; i++)
{
key = (key | caractere_table[pattern[i]]) << 2;
}

return key >> 2;
}





Le vecteur caractere_table est initialisé dans le constructeur de la classe ReferenceSourceHash. C'est un stl::vector de 256 cases.

Une fois recompilé, profilons de nouveau notre programme avec VTune :

 Vue générale




Par ce simple changement, nous avant réduit le temps passé dans cette fonction de 8 secondes.
Voici la vue du code source :

 Vue du code source


Pour conclure :



C'est un cas très simple puisque cette amélioration aurait pu etre identifié avec gprof. Cependant, il a l'avantage de montrer que l'on peut en quelques minutes très facilement identifier les goulets d'étranglements. Intel VTune ne permet pas de trouver directement le bon algorithme mais il vous permet de vous rapprocher de la machine une fois que vous l'avez choisi pour le comprendre, l'évaluer, l'optimiser même dans un contexte où votre programme exploite plusieurs threads.

En disposant d'un outil de mesure, vous pouvez prendre plus facilement les bonnes décisions. Je trouve que la force d'un tel outil se trouve dans le fait que comme avec gprof, vous n'avez pas besoin d'adapter votre code source pour le profiler. L'outil se charge de toute la complexité, vous invitant seulement à l'issue à analyser un rapport clair et exhaustif.

Vous pouvez faire bien plus avec VTune, c'est vraiment un cas très basique (peut etre meme trop) ... Et ce n'est que l'un des nombreux outils de la suite Parallel studio ...

Je trouve cet outil facile à utiliser, je ne pense pas être le seul, mais encore faut il avoir l'idée d'en faire usage. Jusqu'à présent, quand j'entendai parler de la suite Parallel studio dans la presse spécialisée, je me disai qu'il s'agissait d'un outil destiné exclusivement à un milieu de pointe.

Je pense qu'Intel aurait intérêt à organiser des sessions gratuites de quelques heures permettant aux développeurs de découvrir les possibilités offertes par ces outils au travers de cas pratique. D'autres sociétés comme Microsoft ou National Instrument le proposent régulièrement autour de Marseille.

Pour de plus amples informations sur les optimisations de compilation, consultez notre Avertissement concernant les optimisations.