Commit 0d611173 authored by Thibault Soucarre's avatar Thibault Soucarre
Browse files

add report

parent 07ff843c
\section{Annexes}
\newpage
\section{Conclusion}
\newpage
\section{Réalisation du projet}
\subsection{Fonctionnement global}
\begin{itemize}
\item Structure de données / Rendu
\item Explication DrawTrace / GanttDiagramm ?
\item Interface moteur de rendu
\end{itemize}
Afin de bien comprendre le rôle des différentes fonctionnalités attendues d'une classe de rendu graphique, il fallait comprendre le fonctionnement global du logiciel ViTE.
Lors de l'ouverture d'une trace, la première étape est la lecture du fichier par le parser. ViTE peut lire plusieurs formats de fichier (Pajé, Tau, OTF). On utilise donc le parser approprié pour lire le fichier et remplir nos structures de données. Il n'est pas utile de détailler le fonctionnemnt de chaque parseur. Chacun permet de remplir une instance de la classe \textbf{Trace}. C'est cette classe qui sera utilisée ensuite pour réaliser l'affichage graphique. Cette classe contient une liste des containers racines dans l'arbre décrit section \ref{trace}. Chaque container contient une liste de ses containers fils, des arbres contenant ses états et évènements, une liste de liens et une liste de compteurs. C'est à partir de cette structure que le rendu graphique sera construit.
Trois classes sont utilisées pour passer de la structure de données au rendu graphique : DrawTrace, GanttDiagramm et Render\_OpenGL. La classe DrawTrace parcourt la structure et appelle les fonctions de GanttDiagramm pour chaque entité. GanttDiagramm va calculer les coordonnées des entités à afficher et appeler les fonctions de rendu en donnant ces coordonnées en paramètres. Les fonctions du rendu appelées dans ce parcours ne servent pas directement à afficher les entités. Si c'était le cas, il faudrait refaire ce parcours à chaque rafraichissement de l'image, ce qui serait bien trop coûteux. Ces fonctions servent à remplir des structures fournies par OpenGL qu'on pourra ré-utiliser lors de chaque appel à notre fonction d'affichage.
\subsection{Mise à jour du moteur de rendu}
\subsubsection{Moteur de rendu existant}
Dans le cas du rendu existant, Render\_OpenGL, les structures utilisées sont des ``display list'' qui stockent les appels à faire dans la fonction d'affichage. Les fonctions appelées dans le parcours permettent de remplir ces display list. Le rôle de la fonction d'affichage est d'exécuter les appels ainsi enregistrés. Ce moteur de rendu existant était basé sur OpenGL 2.1. Lors de la sortie de la version 3 d'OpenGL, certaines fonctions jugées peu performantes ont été dépréciées, les programmeurs étant invités à ne plus les utiliser. Ces fonctions ont été supprimées plus tard avec la version 3.1.
Certaines des fonctions supprimées dans OpenGL 3 étaient des fonctions très utilisées dans les display lists de l'ancien rendu. En particulier, les fonctions \textbf{glVertex()} et \textbf{glColor()}, servant respectivement à définir la position d'un point dans l'espace et sa couleur ont été supprimées. On utilisait glColor pour définir la couleur courante puis glVertex permettait de créer un point à partir de trois coordonnées passées en paramètres. La carte graphique s'occupait ensuite automatiquement de définir sa couleur et de calculer sa position à l'écran selon des coordonnées. Le moteur de rendu utilisant ces fonctions n'était donc plus compatible avec les dernières versions de la librairie.
Ces fonctions n'étant plus utilisables avec OpenGL 3, il faut gérer manuellement ces paramètres. Ainsi, on peut effectuer une gestion plus adaptée aux particularités du logiciel ViTE, afin d'éviter certaines opérations inutiles.
\subsubsection{Etude d'OpenGL 3}
\begin{itemize}
\item VBO/VAO
\item Shader
\item Matrices
\item Fonction d'affichage
\end{itemize}
Les premières semaines du stage ont donc été consacrées à la découverte d'OpenGL 3, par la réalisation de programmes de test.
OpenGL 3 intoduit deux nouveaux types d'objets qui sont au centre de son fonctionnement : les Vertex Buffer Objects (VBO) et Vertex Array Object (VAO).
Un VBO est un objet OpenGL qui contient des données relatives à un modèle (coordonnées, couleurs, textures...). Les données ne sont pas stockées dans la RAM mais directement dans la mémoire de la carte graphique. L'affichage de rendu OpenGL est géré par la carte graphique. Si les données correspondant à un modèle étaient stockées dans la RAM, il faudrait les transférer à la carte graphique lors de chaque appel à la fonction d'affichage. En utilisant les VBO, on fait ce transfert seulement une fois puis les données restent disponibles dans la carte graphique jusqu'à ce qu'on décide de détruire le VBO.
Un VAO a une utilité similaire à un VBO : il permet également de stocker des informations dans la mémoire de la carte graphique. Sauf qu'ici, ce sont des appels de fonctions qui sont stockés, il s'agit d'appels répétitifs servant à chercher les données stockés dans le VBO.
Une fois les données envoyées à la carte graphique, celle-ci exécute un programme spécifique appelé Shader, qui est appelé une fois pour chaque point. Il est écrit dans un langage spécifique, OpenGL Shading Language (GLSL) et est compilé à l'exécution du programme C++. Il se décompose en deux sous-programmes :
\begin{itemize}
\item VertexShader : Il calcule les coordonnées du point à l'écran.
\item FragmentShader : Il définit la couleur du point.
\end{itemize}
Le VertexShader et le FragmentShader peuvent récupérer en entrée les données définies dans le VBO. Il est aussi possible de définir des variables globales utilisées pour chaque point. En général, on utilise deux matrices dans le VertexShader : la matrice de projection qui permet de passer des coordonnées 3D au plan affiché à l'écran, et la matrice de modèle qui permet d'effectuer des modifications globales sans modifier les coordonnées de chaque point. Par exemple, zoomer revient à effectuer une homothétie sur cette matrice, se déplacer revient à effectuer une translation.
\subsubsection{Implémentation d'un nouveau moteur de rendu}
Dans le but de simplifier le code de la classe de rendu, la gestion des VBOs/VAOs s'effectue grâce aux méthodes d'une classe dédiée, EntityGraphicObject. Il fallait donc écrire cette classe avant de commencer le nouveau rendu. On dresse ici la liste des fonctionnalités de cette classe :
\begin{itemize}
\item Ajout de points dans le VBO
\item Envoi des données vers la carte graphique
\item Verrouillage/Déverrouillage
\end{itemize}
Pour la première de ces tâches, la classe utilise plusieurs listes, basées sur la classe vector de la librairie standard. Celles-ci contiennent les données que l'on veut stocker, les coordonnées et les couleurs.
Ainsi lorsqu'on veut ajouter un vertex, on ajoute ses coordonnées et couleurs à nos listes. Une fois les listes complètes, on génère un VBO et on lui alloue une mémoire correspondant à la somme des tailles de nos listes et on lui envoie les données. On crée ensuite le VAO, et on stock les appels nécessaires.
Les fonctions de verrouillage/déverrouillage sont très simples puisqu'il s'agit seulement d'appeler les fonctions fournies par \textbf{OpenGL}.
Cette classe réalisée, il fallait passer au développement de la classe de rendu en elle-même. Dans le constructeur de cette classe, on crée le Shader qui sera utilisé par notre logiciel. Celui-ci est assez basique, il prend en paramètres les coordonnées et couleur(composantes RGB) du point, ainsi qu'une matrice, qui sera le produit de nos matrices projection et modelview. Il calcule alors la position du point à l'écran et lui assigne la couleur récupérée.
Dans un premier temps, on a utilisé une instance de la classe EntityGraphicObject par catégorie d'entité : Containers, Etats, Liens, Evènements et Variables. La classe devait avoir des méthodes de même prototypes que celles de l'ancien rendu afin de pouvoir passer facilement de l'un à l'autre. Les principales méthodes à implémenter peuvent se séparer ainsi :
\begin{itemize}
\item Les méthodes servant à ajouter une entité au rendu. Ces fonctions sont appelées lors de la lecture de la trace.
\item La fonction d'affichage, qui sera appelée à chaque rafraichissement de l'image.
\end{itemize}
Pour le premier type, il existe une méthode pour chaque type d'entité, qui prend en paramètres des coordonnées permettant de situer l'entité dans la trace. La méthode calcule les coordonnées des différents points qui la compose et les ajoute aux liste de l'EntityGraphicObject correspondant. Pour la couleur, on utilise la couleur courante, un attribut de la classe de rendu. Ainsi, on remplit les listes de nos EntityGraphicObject jusqu'à obtenir les coordonnées de chaque point que l'on veut voir apparaître dans la trace.
Ceci fait, pour chaque instance de EntityGraphicObject, on appelle la méthode permettant la configuration des VBOs et VAOs. On est maintenant prêt à utiliser la fonction d'affichage.
Dans la fonction d'affichage, on modifie la matrice de modelview pour zoomer et se déplacer dans la trace selon les actions de l'utilisateur. On effectue le produit matriciel projection * modelview et on envoie le résultat au Shader. Ensuite on parcourt l'ensemble de nos instances EntityGraphicObject, pour en effectuer l'affichage. Pour celà, on utilise la fonction de verouillage, puis le Shader peut récupèrer les données stockées dans le VBOs et afficher les points concernés. Ensuite, on déverouille et on passe au VBO suivant.
\subsubsection{Analyse des performances}
On obtient ainsi une nouvelle classe de rendu, compatible avec OpenGL 3, et pouvant donc profiter de futures améliorations de la librairie. Il fallait ensuite comparer les performances de ce nouveau rendu avec celles de l'ancien. La consommation mémoire a été comparée à l'aide de l'outil massif de valgrind. L'exécution du logiciel sur une trace est simulée par l'outil massif, et on récupère en sortie un fichier texte permettant notamment de connaître la consommation RAM maximale atteinte au cours de l'exécution, information qui nous importe le plus car c'est la principale limite du logiciel. L'exécution du logiciel a été simulée sur 7 traces de taille variable, avec l'ancien et le nouveau rendu, afin de tracer des courbes d'évolution de la consommation mémoire en fonction de la taille de la trace.
\begin{figure}[!htbp]
\centering
\includegraphics[height=12cm]{img/comp1.png}
\caption{Comparaison des consommations mémoires des deux rendus}
\label{perf1}
\end{figure}
C'est une représentation un peu simpliste, puisque la consommation RAM ne dépend pas uniquement de la taille du fichier d'entrée. Ces courbes, représentées figure \ref{perf1} permettent néanmoins de se donner une idée de l'efficacité de ce nouveau rendu. Ainsi, ce nouveau rendu a une consommation mémoire légèrement supérieure au précédent. Le passage au nouveau rendu permettait donc une compatibilité avec OpenGL 3 mais avec une légère perte de performance. Un des principaux objectifs du stage étant au contraire de réduire le coût en mémoire du logiciel, il fallait donc trouver des optimisations.
\subsection{Optimisation du moteur de rendu}
Dans un premier temps, le programme a été analysé pour déterminer ce qui consommait le plus de mémoire. Au moment de configurer nos VBOs, on affiche le nombre de points stockés. Il est apparu que les états représentaient une bonne partie de la consommation du logiciel. Par exemple, sur un fichier trace d'environ 550 Mo, on représentait près de 60 millions de points, ce qui est bien trop élevé en comparaison de la résolution de nos écrans. C'est donc principalement le stockage des états qu'il fallait optimiser.
\subsubsection{Solutions envisagées}
Pour celà, plusieurs possibilités ont été envisagées.
\begin{itemize}
\item Utiliser plusieurs VBOs pour les états, en les triant selon leur taille. On pourrait ainsi ne les prendre en compte qu'à un niveau de zoom suffisant, réduisant ainsi les calculs effectuées par la fonction d'affichage.
\item Changer le mode d'affichage OpenGL. OpenGL permet de relier les points stockés dans un VBO sous différents modes, notamment GL\_QUADS et GL\_QUADS\_STRIP (cf figure \ref{GLQUADS}). En passant à GL\_QUADS STRIP, on évite de stocker en double les points appartenant à deux rectangles consécutifs.
\item Séparer les états en plusieurs VBOs selon leur valeur (Working, Waiting, Compute...). On ne stocke plus les couleurs dans le VBO mais on crée plusieurs Shaders, un par type d'état. Ces Shaders ne liront pas les couleurs dans le VBO mais donneront à tous les points la même couleur, écrite directement dans le code du FragmentShader.
\end{itemize}
\begin{figure}[!htbp]
\centering
\includegraphics[height=4cm]{img/glQuads.jpg}
\includegraphics[height=4cm]{img/glQuadsStrip.jpg}
\caption{Comparaison des modes GL\_QUADS et GL\_QUADS\_STRIP}
\label{GLQUADS}
\end{figure}
\subsubsection{Choix et Réalisation}
La première solution a rapidemment été éliminée car elle ne permettait pas de changer la consommation mémoire et à un niveau de zoom élevé, on ne gagnait plus en calculs.
La deuxième solution présentait un problème : un point n'étant associé qu'à une couleur, on ne peut pas représenter ainsi les points appartenant à deux rectangles consécutifs de couleurs différentes.
C'est finalement la troisième solution qui a été retenu. La séparation des états selon leur valeur présentait également l'avantage de faciliter le changement de couleurs via le menu settings qui a été réalisé plus tard.
De plus, les coordonnées précedemment représenté par des \textbf{double} sont devenus des \textbf{float}. En effet le type \textbf{double} n'est pas disponible en glsl, il y avait donc conversion en \textbf{float} lors de l'envoi des données au Shader. Ainsi le changement de type ne cause pas de perte de précision et permet de réduire la consommation mémoire.
Pour réaliser cette amélioration, il a fallu surchagé la méthode permettant d'ajouter un point à un VBO : plus besoin de prendre les paramètres correspondant à la couleur pour les états. L'ancienne méthode devait rester disponible pour les autres types d'entités. Mais surtout il a fallu revoir la gestion des Shaders : dans un premier temps, on a utilisé un shader unique, stockée dans la classe de rendu, et il fallait maintenant gérer plusieurs shaders, un par type d'états.
Pour gérer cela deux maps ont été ajoutées comme attributs de la classe de rendu. La clé utilisée est la même : la valeur de l'entité. En fonction de cette clé, on peut récupérer l'EntityGraphicObject et le Shader correspondants. Ainsi le fonctionnement de la fonction draw\_state, servant à ajouter un état au rendu a été revu.
On commence par vérifier si nos maps contiennent un élément dont la clé est égale à celle passée en paramètre de la fonction. Si c'est le cas, on ajoute nos points au VBO correspondant à cette clé.
Sinon, on crée une nouvelle instance de notre classe EntityGraphicObject qu'on ajoute à notre map. On crée également un nouveau Shader, qui associe à chaque point la même couleur, celle qui est stockée comme couleur courante. Pour cela, dans le constructeur de la classe Shader, on manipule une chaîne de caractères correspondant au code du FragmentShader, dans laquelle on insère les valeurs rgb voulues.
Dans la fonction d'affichage, on parcourt nos maps et on active successivement nos différents Shaders, et on affiche les EntityGraphicObject correspondant.
\subsubsection{Analyse des performances}
De nouvelles courbes de consommation mémoires ont été tracées, suivant le même protocole que précedemment, pour mesurer le gain en performance obtenu. Ces courbes sont représentées figure \ref{perf2}. On voit qu'on obtient une baisse d'environ 30\% de la consommation mémoire par rapport à l'ancien rendu. De plus, on constate à l'utilisation une meilleure fluidité du logiciel lors de la mise à jour du rendu suite à un zoom ou un déplacement.
\begin{figure}[!htbp]
\centering
\includegraphics[height=12cm]{img/comp2.png}
\caption{Comparaison des performances}
\label{perf2}
\end{figure}
\subsection{Refonte du menu Settings}
Le logiciel ViTE propose également un menu Settings qui permet de modifier les couleurs associées aux différentes entités représentées. On associe une couleur aux différentes entités puis n recharge la trace en relisant le fichier comme lors de l'ouverture, mais si une couleur est définie dans le menu, on prend cette couleur à la place de celle qui est écrite dans le fichier. Cet algorithme était très peu efficace : il nécessite une relecture du fichier, bien plus longue qu'un appel à la fonction d'affichage. De plus, il existe bien souvent de très nombreux états de valeur identique. Ici, la vérification de la couleur s'effectue à l'intérieur du parcours, donc une fois par état.
L'optimisation expliquée au paragraphe précédent permet également la mise en place d'un fonctionnement bien plus efficace de ce menu. Plus besoin de relire le fichier, il suffit simplement de supprimer le Shader associée au type d'entité que l'on veut modifier, et de le remplacer par un nouveau Shader, qui associe à chaque point la nouvelle couleur. Ainsi, la modification est presque instantanée, quelque soit la taille de la trace visualisée.
\subsubsection{Correction de l'interface}
Un autre problème de ce menu était le mauvais fonctionnement du bouton cancel, ce dernier ayant le même effet que le bouton OK. Pour cela les changements effectué dans le menu sont stockés dans une map dont la clé est le nom du type d'entité et la valeur la nouvelle couleur. Le remplacement de Shader ne s'effectue qu'au moment où l'utilisateur clique sur le bouton ``Apply'', on parcourt la map et on effectue les remplacements nécessaires. Lors d'un clic sur ``Cancel'', il suffit de vider cette map.
\subsubsection{Autres entités}
Cependant, ce nouveau fonctionnement ne pouvait être utilisé que pour les états, les autres entités utilisant encore un Shader unique. Afin de rectifier cela, les mêmes modifications ont été appliquées aux liens et évènements.
%\subsection{Divers}
\subsection{Validation et Résultats}
\newpage
\section{Glossaire}
\newpage
\section{Présentation de l'Inria}
\newpage
\section{Introduction}
\newpage
\section{Présentation du sujet de stage}
\subsection{Trace d'exécution}
\label{trace}
Depuis quelques années, la fréquence des processeurs tend à stagner. L'augmentation de la puissance de nos ordinateurs passe de plus en plus par le développement d'architecture multicoeurs : on augmente non plus la fréquence mais le nombre de processeurs présents dans une machine. Afin de tirer profit de cette puissance, le recours au traitement parallèle devient une nécessité.
Les traces d'exécution constituent un outil d'aide au développement et à la corrections d'application parallèles. Elles permettent d'observer l'état des différentes unités de calcul utilisées au cours, et les communications entre celles-ci au cours d'une exécution. Ainsi on peut repérer des erreurs de synchronisation, d'accès concurrents ou encore repérer quelles sections d'un programme sont mal réparties entre nos unités de calcul. \\
Le format Pajé est le format le plus utlilisé pour la représentation de traces. Une trace au format Pajé est composé d'objets organisés selon un arbre hiérarchique de types. Les noeuds sont appelés \textbf{Containers}, et les feuilles \textbf{entités}. Les entités sont des objets simples : ils ne peuvent pas contenir d'autres entités. Elles se répartissent en 4 catégories : les \textbf{états}, les \textbf{liens}, les \textbf{évènements}, et les \textbf{compteurs}. Ces différents objets sont définis ci-après :
\begin{itemize}
\item \textbf{Container : }Il représente une unité de calcul matériel ou logiciel . Ce peut être un processeur, un processus ou encore un thread. Un container peut contenir d'autres objets (appelés fils) y compris d'autres containers. Ainsi la hiérarchie de types peut s'étendre sur plusieurs niveaux.
\item \textbf{Etat : }Il est associé à un container (son père) et est représenté sur la même ligne que celui-ci. Il possède un temps de début et un temps de fin. Il représente une période de temps durant laquelle le container père reste dans le même état.
%La figure \ref{states} représente une trace simple ne contenant que des containers et des états. Les containers, en bleu, représente les processeurs utilisés. Les états noirs correspondent à un temps d'attente du container père, les états rouge à une période ou celui-ci effectue des calculs. Les types d'états sont en réalité plus nombreux et on pourrait utiliser plus de couleurs pour les différencier.
%\begin{figure}[!htbp]
%\centering
%\includegraphics[height=8cm]{img/states.jpg}
%\caption{Etats}
%\label{states}
%\end{figure}
\item \textbf{Liens : }Il représente une relation entre deux containers qui possède un temps de début et un temps de fin. Ils peuvent être utilisés pour représenter des communications entre processus/thread. Ils sont représentés sous forme de flèches.
%, comme sur la figure \ref{links}.
%\begin{figure}[!htbp]
%\centering
%\includegraphics[height=8cm]{img/links.jpg}
%\caption{Liens}
%\label{links}
%\end{figure}
\item \textbf{Evènement : }Comme les états, ils sont associés à un container père. Un évènement représente une action instantanée effectuée par le container père.
%Sur la figure \ref{events}, on a zoomé sur une zone de la trace contenant de nombreux évènements.
%\begin{figure}[!htbp]
%\centering
%\includegraphics[height=8cm]{img/events.jpg}
%\caption{Evènements}
%\label{events}
%\end{figure}
\item \textbf{Compteur : }Egalement associé à un container père, il représente l'évolution numérique d'une variable au cours du temps. Ils sont représentés sous forme de courbes.
%La figure \ref{variables} représente une trace ne contenant pas d'états, mais avec des compteurs.
%\begin{figure}[!htbp]
%\centering
%\includegraphics[height=8cm]{img/variables.jpg}
%\caption{Compteurs}
%\label{variables}
%\end{figure}
\end{itemize}
\subsection{Le logiciel ViTE}
Le logiciel ViTE (Visual Trace Explorer) est un logiciel de visualisation, il ne sert pas à générer des traces. Il permet de parser un fichier trace existant et d'en obtenir une représentation graphique. Plusieurs formats de traces sont supportés (OTF, Tau, Pajé) et le logiciel est disponible sur divers systèmes d'exploitation : Windows, MacOS X, Ubuntu et divers autres distributions de Linux. Il est possible de zoomer sur une zone de la trace, de se déplacer, de modifier les couleurs associées aux différents types d'entités représentés. Ces traces doivent donc avoir été générées précedemment par exemple en utilisant le logiciel Eztrace, égalemment développé par l'Inria. \\
\subsection{Objectifs du stage}
Le premier objectif du stage est la mise à jour du moteur de rendu de ViTE. Le rendu s'appuie sur la librairie OpenGL dont la version 3 a apporté de nombreuses modifications. Certaines fonctions ont été supprimés, rendant ViTE incompatible avec les versions récentes.
L'objectif principal est ensuite d'exploiter au mieux les nouvelles possibilités offertes par OpenGL 3, afin de corriger le principal défault du précédent rendu graphique : une consommation mémoire trop importante qui empêche la visualisation de grandes traces d'exécution. Il faudra donc réaliser un nouveau rendu plus optimisé, capable d'afficher des traces de taille importante, en limitant la consommation mémoire.
Un autre objectif est l'amélioration l'expérience utilisateur. Il fallait ainsi obtenir plus de fluidité dans la manipulation du rendu : zoom, déplacement, modification de couleurs, masquage de certains éléments.
Enfin, il fallait corriger les bugs rencontrés, réorganiser certaines parties du code afin de le rendre plus clair.
\subsection{Critères de validation}
L'objectif principal étant la diminution de la consommation mémoire, celle-ci doit être mesurée et comparée avec celle de la version précédente. L'outil employé a été massif, un profiler mémoire de la suite valgrind, qui permet de savoir quelle quantité la valeur du pic de consommation mémoire au cours d'une exécution. Ainsi, on peut comparer sur divers traces la mémoire utilisée par les deux versions et vérifier que celle-ci a bien diminuée. La possibilité d'ouvrir des traces de taille plus importante que précedemment peut également être vérifiée.
En revanche, il est difficile de réaliser des tests automatisés sur des éléments tels que la validité du rendu graphique obtenu, sa fluidité ou encore le bon fonctionnement de l'interface. Ces éléments ont donc fait l'objet de retour d'utilsateurs se servant régulièrement du logiciel.
\subsection{Plannification du projet}
La première partie du stage a été consacrée à la compréhension globale du logiciel et à l'apprentissage de la librairie \textbf{OpenGL}. Ensuite, il a fallu réaliser le nouveau rendu et optimiser ses performances. Enfin, le menu settings a été revu, le nouveau rendu permettant la mise en place de fonctions plus efficaces.
\newpage
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment