• Aucun résultat trouvé

Lors de cette étude préliminaire, nous avons développé un prototype permettant d’analyser l’exécution du programme et repérer les fonctions potentiellement impactées par les cas pa- thologiques étudiés. Ce dernier est rapidement discuté ici afin de montrer qu’il est possible de détecter ces problèmes de manière dynamique.

3.6.1 Objectifs

Dans ce chapitre, nous avons montré que les paramètres clés des problèmes étudiés étaient l’adresse de base et le nombre de tableaux utilisés simultanément dans les boucles. Notre outil se propose donc d’extraire ces paramètres pour chaque fonction d’une application.

Ici, nous ne nous intéressons qu’aux grands segments mémoires, au-delà de 4 Ko. De plus, ne seront analysés que les segments dynamiques alloués par le biais des fonctions malloc, calloc et realloc. Notre approche sera basée sur une instrumentation de l’exécutable et une collecte d’information pendant l’exécution du programme. Afin de ne pas tomber dans des problèmes de volumes de données, nous limiterons la prise d’information à une forme échantillonnée. Les entités fondamentales seront constituées des fonctions et des blocs alloués. Nous n’analyserons donc pas les accès détaillés à l’intérieur des blocs eux-mêmes ou bien la prise en compte de présence de plusieurs boucles dans une même fonction.

Cette approche simplifiée a permis d’obtenir rapidement un prototype fonctionnel permet- tant de pointer les fonctions potentiellement sensibles aux effets recherchés. Elle a toutefois le défaut de pouvoir déclencher de faux positifs.

3.6.2 Points techniques sur la méthode

Notre objectif est de collecter des informations sur l’utilisation des tableaux dynamiques. Nous avons donc utilisé la démarche suivante :

Instrumentation du binaire : La première étape consiste à instrumenter chaque appel de fonc-

tion de l’exécutable et les bibliothèques éventuelles avec l’option -finstrument-functions6 du compilateur GCC. De cette manière notre outil est notifié à chaque appel de fonction afin de pouvoir attacher les tableaux utilisés à une pile d’appels.

Suivi des gros tableaux : Les fonctions malloc,calloc,realloc et free sont ré-implémentées par la

bibliothèque d’analyse afin de marquer les allocations mémoires supérieures à un seuil pour suivre leur utilisation et connaître leur taille.

Suivi des accès : Le suivi d’accès aux tableaux surveillés est réalisé à l’aide de captures des

signaux de type faute de segmentation. Pour cela, lors de l’entrée dans une fonction, l’outil d’instrumentation rend les tableaux inaccessibles à l’aide de l’appel système mprotect. Lors d’un accès, la faute est capturée, l’accès noté pour la fonction en cours et le tableau entier est rendu accessible. Il est possible d’analyser les accès à la granularité de la page, mais par souci de simplicité nous avons limité notre prototype au grain des tableaux complets.

3.6. Outil d’analyse

Chaque entrée/sortie de fonction nécessite une remise en place des verrous d’accès aux tableaux.

Système de liste noire : Avec les points précédents nous générons potentiellement un nombre

trop important de données et d’appels système donc un surcoût d’exécution important proportionnel au nombre d’entrées/sorties de fonction. Pour simplifier le problème, nous avons appliqué une méthode d’échantillonnage limitant le nombre d’analyses réalisées sur une fonction donnée. Une fois ce seuil d’analyse dépassé, la fonction concernée est placée dans la liste noire et ne nécessitera plus de verrouillage d’accès aux tableaux. Le programme est donc fortement ralenti lors des premiers appels de fonction puis reprend une cadence normale d’exécution uniquement impactée par le surcoût d’instrumentation. Pour être efficace, la liste noire dispose de l’équivalent d’un cache maintenant les dernières entrées récemment utilisées.

Avec cette approche sélective et une implémentation naïve des fonctions de liste noire, nous obtenons un surcoût d’un facteur 2 pour de petites exécutions d’EulerMHD. Ce dernier se réduit toutefois à 30% pour des cas tests de plus grande taille toujours avec EulerMHD. L’approche retenue à base de signaux de type faute de segmentation implique toutefois que le prototype n’est pas utilisable en contexte multithread. Les protections des segments sont en effet globales au processus. Il n’est donc pas possible de maintenir un verrouillage des accès pour un thread donné. Cette approche nous permet néanmoins de trouver rapidement les fonctions à problème pour les programmes séquentiels et MPI.

Comme discuté, la méthode précédente n’est pas applicable sous cette forme en context parallèle. Pour supporter ce type d’application il faudrait recourir à une émulation (par exemple avec valgrind[NS07]) ou instrumentation des accès (MAQAO[BCRJ+10], Pintool[HLC09]). Une autre possibilité serais d’exploiter certaines propriétés de la segmentation mais elle n’est plus disponibles sur les architectures intel 64 bits. Dans le cadre de notre étude, nous nous somme limité au prototype séquentiel.

3.6.3 Informations collectées

Code 3.6– Exemple d’informations obtenues avec l’outil de trace.

1 = = = = = = = = = = = = = = = = = = = = A L L O C = = = = = = = = = = = = = = = = = = = = = = = = 2 m a i n() [t e s t.c: 5 5 ] 3 - B l o c 0 of s i z e 128Ko [ 16 , 16 ] 4 - B l o c 1 of s i z e 128Ko [ 16 , 16 ] 5 - B l o c 2 of s i z e 128Ko [ 16 , 16 ] 6 = = = = = = = = = = = = = = = = = = = = F R E E = = = = = = = = = = = = = = = = = = = = = = = = 7 m a i n() [t e s t.c: 5 5 ] 8 - B l o c 0 of s i z e 128Ko 9 - B l o c 1 of s i z e 128Ko 10 - B l o c 2 of s i z e 128Ko 11 = = = = = = = = = = = = = = = = = = = = MEM U S A G E = = = = = = = = = = = = = = = = = = = = = = = = 12 m a i n() [t e s t.c: 5 5 ] { 1 : 2 3 . 3 7 5 9 % } 13 - U s i n g 0 of s i z e 128Ko [ 16 , 16 ] { + 0 } 14 - U s i n g 1 of s i z e 128Ko [ 16 , 16 ] { + 0 } 15 - U s i n g 2 of s i z e 128Ko [ 16 , 16 ] { + 0 } 16 t e s t 2() [t e s t.c: 2 2 ] { 1 : 2 0 . 5 9 9 6 % } 17 - U s i n g 1 of s i z e 128Ko [ 16 , 16 ] { + 1 } 18 - U s i n g 0 of s i z e 128Ko [ 16 , 16 ] { + 0 } 19 - U s i n g 2 of s i z e 128Ko [ 16 , 16 ] { + 1 } 20 t e s t 3() [t e s t.c: 1 2 ] { 1 : 8 . 2 4 6 9 6 % } 21 - U s i n g 2 of s i z e 128Ko [ 16 , 16 ] { + 0 } 22 - U s i n g 1 of s i z e 128Ko [ 16 , 16 ] { + 0 } 23 t e s t 1() [t e s t.c: 3 2 ] { 1 : 4 7 . 7 7 7 5 % } 24 - U s i n g 0 of s i z e 128Ko [ 16 , 16 ] { + 0 }

Avec les données collectées, il est possible d’extraire un résumé d’utilisation des tableaux pour chacune des fonctions. Sur un simple programme test, il est par exemple possible d’obtenir la sortie du code3.6. Cette sortie fournit les informations suivantes :

Section ALLOC ligne 1-5 : Liste les allocations effectuées par les différentes fonctions. Chaque

allocation est alors associée à un ID unique utilisé pour suivre son historique et caractérisé par sa taille. Les nombres entre crochets donnent respectivement les alignements relatifs aux pages de 4Ko et 2Mo, permettant une analyse rapide des problèmes d’alignement.

Section FREE ligne 6-10 : Donne les points de libérations des tableaux.

Section MEM USAGE ligne 11-24 : Cette section liste les tableaux utilisés par les différentes

fonctions du programme en donnant le nombre d’appels de ces fonctions et le pourcen- tage de temps consommé par ces dernières. Sont alors listés chacun des tableaux utilisés identifiés par leur ID et en rappelant leurs paramètres (taille, alignement). Ces informa- tions sont complétées par le décalage du premier élément utilisé pouvant potentiellement, permettre une détection de certains problèmes de conflits lecture/écriture décrits en sec- tion3.4.4.

On peut alors générer un graphique plaçant les points de chaque fonction dans un repère construit sur le nombre de tableaux accédés et le temps relatif des fonctions. Il est ainsi possible de repérer rapidement la présence de zones à problème ayant un impact potentiellement signi- ficatif sur les performances (nombre de tableaux supérieur à l’associativité avec un temps relatif important). Sur EulerMHD on constate ainsi aisément la présence d’une fonction exploitant 9 tableaux et occupant plus de 50% du temps d’exécution (figure3.3.4). Cette visualisation peut également permettre de retrouver rapidement les fonctions impactées en comparant deux exé- cutions superposées sur le même graphique, l’une utilisant des alignements standards, l’autre des alignements aléatoires. Il pourrait bien sûr être envisagé de construire un greffon de ce type pour Valgrind en bénéficiant des bases fournies par cet outil. L’important dans notre approché est de profiter des adresses de base et tailles de bloc extraites de l’allocateur mémoire. Ces infor- mations permettent d’associer une certaine sémantique aux adresses accédées par le programme en ne pas les considérer comme de simples données éparses.