rendu du rapport

git-svn-id: http://cregut.svn.enseeiht.fr/2020/1sn/pim/projets/GH-05@212385 e13453a9-b01f-0410-a051-f404c4f0c485
This commit is contained in:
lfainsin 2021-01-16 16:31:00 +00:00
parent 8eb3244392
commit 0cc020a79d
2 changed files with 66 additions and 48 deletions

View file

@ -14,6 +14,8 @@
\newminted{ada}{linenos, numbersep=6pt, frame=leftline, xleftmargin=6mm, firstnumber=last}
\newminted{bash}{numbersep=6pt, frame=leftline, xleftmargin=6mm}
\setlength{\parskip}{0.2cm}
\usepackage[
top=1.5cm,
bottom=1.5cm,
@ -42,16 +44,14 @@
\section{Introduction}
Lors de ce projet nous avons implémenté un algorithme permettant de caculer le PageRank pour un réseau donné.
Nous l'avons construit à partir de la methode des raffinages. De même, nous utilisons le langage de programmation compilé Ada.
Au cours de ce projet, nous avons implémenté un algorithme permettant de calculer le PageRank pour un réseau donné. Nous lavons construit à partir de la méthode des raffinages. De plus, nous utilisons le langage de programmation compilé Ada.
\section{Résumé du sujet}
Le PageRank est un algorithme d'analyse des liens entre des pages Web dans un réseau, il mesure la popularité des pages dans celui-ci. En outre il trie les pages internet de la plus populaire à la moins populaire selon des principes simples:
Le PageRank est un algorithme danalyse des liens entre des pages Web dans un réseau, il mesure la popularité des pages dans celui-ci. En outre, il trie les pages internet de la plus populaire à la moins populaire selon des principes simples :
\begin{itemize}
\item Une page est respectable si d'autre page la reference, et d'autant plus si elle meme sont populaire.
\item Néanmois il faut ajuster la popularité selon la page qui reference, plus une page possède de lien vers d'autres sites, moins ses référencements ont de valeur.
\item Une page est respectable si dautres pages référencent, et dautant plus si ces dernières elles même sont populaires.
\item Néanmoins, il faut ajuster la popularité selon la page qui référence, plus une page possède de lien vers dautres sites, moins ses référencements ont de valeur.
\end{itemize}
\begin{figure}[ht!]
@ -60,75 +60,73 @@ Le PageRank est un algorithme d'analyse des liens entre des pages Web dans un r
\caption{Exemple d'un réseau et de ses poids calculés via le pageRank \cite{wiki_pageRank}. \label{fig : ex_pageRank}}
\end{figure}
À l'aide du théoreme de point fixe, il peut se résume en un calcul répété d'un produit vecteur-matrice.
À laide du théorème du point fixe, il peut se résumer en un calcul répété dun produit vecteur-matrice.
Toute la difficulté provient de la taille massive que peut avoir le réseau. Il faut donc choisir les bonnes de structures de données. Cet algorithme est notamment utilisé par le moteur de recherche Google qui estime son propre réseaux à une cinquantaine de milliards de pages.
Toute la difficulté provient de la taille massive que peut avoir le réseau. Il faut donc choisir les bonnes structures de données. Cet algorithme est notamment utilisé par le moteur de recherche Google qui estime son propre réseau à une cinquantaine de milliards de pages.
\newpage
\section{Architecture du programme}
Nous pouvons diviser notre programme en trois sous-parties qui s'occuperons de gérer les différents type ainsi que la bonne execution du programme. De même, mise à part pour la récupération des arguments dans la ligne de commande, nous adopterons un style de programmation offensif puisque l'utilisateur n'intéragit jamais avec le programme.
Nous pouvons diviser notre programme en trois sous-parties qui s'occuperont de gérer les différents types ainsi que la bonne exécution du programme. De même, mise à part pour la récupération des arguments dans la ligne de commande, nous adopterons un style de programmation offensif puisque lutilisateur n'interagit jamais avec le programme.
\subsection{Gestion des vecteurs}
Il est aussi à noter que nous avons choisi dutiliser des "declare" plutôt que des sous-procédures auxquelles on ferait appel, puisque cela permet davoir une lecture plus linéaire du code.
Ada est un langage fortement typé, comme nous manipulons ici plusieurs types de données, il est logique de créer un type "Vector" générique dont l'unique but sera de stocker des informations. Nous aurons aussi besoin à plusieurs moments lors du calcul du pagerank de trier, sommer, initialiser des Vectors, d'où le choix de placer cette structure de données dans son propre module. \\
\subsection{Gestion des vecteurs (vector.ads)}
Lorsque nous trierons les données nous ferons appelle à l'algorithme QuickSort. \\
Ada est un langage fortement typé. Comme nous manipulons ici plusieurs types de données, il est logique de créer un type "Vector" générique dont lunique but sera de stocker des informations. Nous aurons aussi besoin à plusieurs moments lors du calcul du pagerank de trier, sommer, initialiser des Vectors, doù le choix de placer cette structure de données dans son propre module.
Nous avons choisi de séparer les vecteurs d'entiers des vecteurs de flottants, car créer un seul et unique module permettant la gestion d'entiers ou de flottants était trop compliqué. Le code aurait été plus compliqué à lire. Nous avons alors choisi de séparer comme ceci le code, bien que cela produise du code un peu redondant.
Lorsque nous trierons les données, nous ferons appel à lalgorithme QuickSort.
\subsection{Formatage du code}
Nous avons séparé le module Vector en trois sous-modules : Un module capable de stocker des entiers, un autre pour des flottants et un dernier pour des liens. Nous avons fait ce choix car créer un unique module générique Vector pour gérer ces trois types de données (très différents) était trop compliqué. Cela était tout de même faisable mais le code était compliqué à lire. Ainsi bien que le code soit quelque fois redondant, il est plus compréhensible. 
Nous avons choisis d'utiliser des "declare" plutot que des sous-procédures dont on ferait l'appelle puisque cela permet d'avoir un lecture plus linéaire du code.
\subsection{Gestion des matrice de Google (google\_*.ads)}
\subsection{Gestion des matrice de Google}
Nous devrons regrouper dans des modules génériques le code en rapport avec la gestion des matrices de Google. Nous utiliserons deux modules, un gérant les matrices naïves et un autre pour gérer les matrices creuses.
Nous devrons regrouper dans des modules génériques le code en rapport avec la gestion des matrices de Google. Nous pourrons deux module, un gérant les matrices naives et un autre pour gérer les matrices creuses.
Ces modules introduirons le type T\_Google, ainsi que des procédure et fonctions permettant de générer la matrice G, nécéssaire au calcul du pageRank, mais aussi l'opération de multiplication entre un vecteur et une matrice.
Ces modules introduiront le type T\_Google, ainsi que des procédures et fonctions permettant de générer la matrice G, nécessaire au calcul du pageRank. Ils implémenteront aussi lopération de multiplication entre un vecteur et une matrice.
\subsection{Gestion du calcul du Pagerank}
\subsection{Gestion du calcul du Pagerank (pagerank.adb)}
Cette dernière partie s'occupe de regrouper tous les éléments présents dans les deux modules cités précédemment pour ainsi calculer itérativement le pageRank du réseaux. Cette sous-partie gère de plus le traitement des arguments de la ligne de commande ainsi que la lecture et l'écriture des résultats dans des fichiers.
Cette dernière partie soccupe de regrouper tous les éléments présents dans les deux modules cités précédemment pour ainsi calculer itérativement le pageRank du réseau. Cette sous-partie gère de plus le traitement des arguments de la ligne de commande ainsi que la lecture et lécriture des résultats dans des fichiers. 
\section{Structures de données}
Nous avons besoin d'une structure pour stocker le poids de chaque pages (i.e. sa popularité), nous appellerons cette structure "pi".
De même, nous avons besoin d'une structure pour stocker le réseau "network" décrivant les liens entre chaque pages du réseau.
Enfin il nous faudra créer une structure de données adapté à notre utilisation pour stocker la matrice de Google, notée G.
Nous avons besoin dune structure pour stocker le poid de chaque pages (i.e. sa popularité), nous appellerons cette structure "pi". De même, nous avons besoin dune structure pour stocker le réseau "network" décrivant les liens entre chaque page du réseau. Enfin il nous faudra créer une structure de données adaptée à la taille du réseau pour stocker la matrice de Google, notée G.
Nous connaissons à l'avance les tailles des différentes structures, car le réseau est connu. Il n'est donc pas nécéssaire de créer des types dynamiques (i.e. liste chainées, vecteurs à taille variable stockés dans le heap...), un simple stockage statique dans le stack suffit.
Nous connaissons à lavance les tailles des différentes structures, car le réseau est connu. Il nest donc pas nécessaire de créer des types dynamiques (i.e. liste chainées, vecteurs à taille variable stockés dans le heap...), un simple stockage statique dans le stack suffit.
Notons N le nombre de pages dans le réseau, et N\_Links le nombre de liens total dans le réseau.
Ainsi pour stocker pi, nous choissons la structure d'un vecteur statique (array) de dimension 1xN.
Ainsi pour stocker pi, nous choisissons la structure dun vecteur de flottants de dimension 1xN.
De même, pour stocker network nous choisissons la structure d'un vecteur de T\_Links de dimension 1xN\_Links (avec T\_Links un enregistrement permettant le stocker les informations d'un liens).
De même, pour stocker network nous choisissons la structure dun vecteur de T\_Links de dimension 1xN\_Links (avec T\_Links un enregistrement permettant de stocker les informations dun lien).
Pour stocker G, la matrice de Google, nous avons 2 choix:
Pour stocker G, la matrice de Google, nous avons 2 choix :
\begin{itemize}
\item Une Matrice naive
\item Une Matrice naïve
\item Une Matrice creuse (éventuellement compressée)
\end{itemize}
\subsection{Implémentation Naive}
Le premier choix consiste à stocker la matrice très naivement, c'est-à-dire en stockant l'ensemble de ses valeurs dans une matrice de taille NxN.
\subsection{Implémentation Naïve}
L'avantage principale de cette structure est que sa construction et sa manipulation (produit vecteur-matrice) est simple.
Pour la construire à partir du réseau nous assignons à chaque lien une valeur dans la matrice. De même celle-ci à l'avantage d'être robuste par défaut à la présence de doublons dans le réseau.
Cette première implémentation consiste à stocker la matrice très naïvement, cest-à-dire en stockant lensemble de ses valeurs dans une matrice de dimension NxN.
L'inconvénient est que nous stockons beaucoups de valeurs inutiles (i.e. des éléments qui se répètent ou bien qui sont égaux à 0). Ainsi puisqu'un produit vecteur-matrice est de compléxité N\^2, nous perdons beaucoup de temps à effectuer des opérations inutiles.
Lavantage principale de cette structure est que sa construction et sa manipulation (produit vecteur-matrice) est simple. Pour la construire à partir du réseau nous assignons à chaque lien une valeur dans la matrice. De même, celle-ci a lavantage par construction dêtre robuste par défaut à la présence de doublons dans le réseau.
Linconvénient est que nous stockons beaucoup de valeurs inutiles (i.e. des éléments qui se répètent ou bien qui sont égaux à 0). Ainsi, puisquun produit vecteur-matrice est de complexité $\text{N}^2$, nous perdons beaucoup de temps à effectuer des opérations inutiles
\subsection{Implémentation Creuse}
Il faut alors nous orienter vers une autre solution si l'on souhaite être plus efficace.
Pour alléger la taille mémoire de la matrice G, nous pouvons la rendre creuse, c'est-à-dire de ne pas stocker les valeurs nulles de celle-ci.
Il faut alors nous orienter vers une autre solution si lon souhaite être plus efficace.
Cependant nous pouvons aller encore plus loin en compressant G via un algorithme de compression simple appelé CSR \cite{wiki_CSR}.
Pour alléger la taille mémoire de la matrice G, nous pouvons la rendre creuse, cest-à-dire ne pas stocker les valeurs nulles de celle-ci. Cependant nous pouvons aller encore plus loin en compressant G via un algorithme de compression simple appelé CSR \cite{wiki_CSR}.
De cette manière nous ne gardons que les informations qui sont essentielles.
L'inconvénient de cette méthode est que la création de ce type de matrice et son utilisation est plus compliqué que pour une matrice naive.
Linconvénient de cette méthode est que la création de ce type de matrice et son utilisation sont plus compliquées que pour une matrice naïve.
Mais l'avantage de cette structure de donnée est son gain d'espace non négligeable ainsi que la rapidité qu'elle propose. En effet, nous n'effectuons plus que N\_Links opérations lors du produit vecteur-matrice, au lieu de N**2 pour la version naive. De même nous, nous stockons uniquement des entiers dans G, au lieu de nombres flottants pour la version naive.
Mais lavantage de cette structure de données est son gain despace non négligeable ainsi que la rapidité quelle propose. En effet, nous neffectuons plus que N\_Links opérations lors du produit vecteur-matrice, au lieu de $\text{N}^2$ pour la version naïve. De même, nous pouvons aussi nous permettre de stocker uniquement des entiers dans G, ce qui diminue encore plus la complexité spatiale.
\section{Benchmark}
@ -151,17 +149,20 @@ ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
\end{bashcode}
Comme le heap n'est pas utilisé par le programme (mis à part pour quelques variables dont nous n'avons pas le controle direct), sachant le PID du programme, nous pouvons inspecter le fichier suivant pour connaitre la taille qu'occupe le programme dans le stack:
Comme le heap nest pas utilisé par le programme (mis à part pour quelques variables dont nous navons pas le contrôle direct), sachant le PID du programme, nous pouvons naïvement inspecter le fichier suivant pour connaître la taille quoccupe le programme dans le stack :
\begin{bashcode}
$ build/pagerank fichiers_test/Exemple_sujet/exemple_sujet.net -n & { while [[ -f /proc/$!/smaps ]]; do grep -A 1 stack /proc/$!/smaps >> stack.txt; done } ; tail stack.txt
$ build/pagerank fichiers_test/Exemple_sujet/exemple_sujet.net -n & \
{ while [[ -f /proc/$!/smaps ]]; do grep -A 1 stack /proc/$!/smaps >> stack.txt; done } ; \
tail stack.txt
\end{bashcode}
\newpage
\subsection{Version Naive}
\subsection{Version Naïve}
\subsubsection{exemple\_sujet.net}
Stack size: 132 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Exemple_sujet/exemple_sujet.net -n
real 0m0,009s
@ -170,6 +171,7 @@ sys 0m0,006s
\end{bashcode}
\subsubsection{worm.net}
Stack size: 1252 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Worm/worm.net -n
real 0m0,073s
@ -178,6 +180,7 @@ sys 0m0,009s
\end{bashcode}
\subsubsection{brainlinks.net}
Stack size : 1570492 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Brainlinks/brainlinks.net -n
real 2m41,387s
@ -185,9 +188,14 @@ user 2m41,074s
sys 0m0,280s
\end{bashcode}
\subsubsection{Linux26.net}
Le programme utilise trop de place dans le stack, il ne peut donc pas être exécuté.
On peut tout de même estimer son espace mémoire à au moins 650 Go.
\subsection{Version Creuse}
\subsubsection{exemple\_sujet.net}
Stack size: 132 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Exemple_sujet/exemple_sujet.net
real 0m0,168s
@ -196,6 +204,7 @@ sys 0m0,009s
\end{bashcode}
\subsubsection{worm.net}
Stack size: 132 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Worm/worm.net
real 0m0,034s
@ -203,7 +212,10 @@ user 0m0,016s
sys 0m0,004s
\end{bashcode}
\newpage
\subsubsection{brainlinks.net}
Stack size: 14748 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Brainlinks/brainlinks.net
real 1m55,939s
@ -212,6 +224,7 @@ sys 0m0,012s
\end{bashcode}
\subsubsection{Linux26.net}
Stack size: 41152 kB
\begin{bashcode}
$ time build/pagerank fichiers_test/Linux26/Linux26.net
real 437m38,234s
@ -221,20 +234,25 @@ sys 0m0,440s
\section{Conclusion}
Nos programmes ne génèrent aucune erreurs selon valgrind et s'éxecutent pour chacuns des réseaux (mise à part pour Linux26 dans le cas naif, mais cela semble normal au vu de la taille du réseau). \\
On remaque facilement la supériorité temporelle et spatiale de la version creuse contre la version naive, surtout lorsque N et N\_links sont grands.
Nos programmes ne génèrent aucune erreur selon Valgrind et s'exécutent pour chacun des réseaux (mis à part pour Linux26 dans le cas naïf, mais cela semble normal au vu de la taille du réseau).
Les fichiers .org et .p quils génèrent sont aussi pratiquement identiques à ceux fournis par lénoncé. Il y a parfois quelques différences dans les fichiers .ord car certaines pages ont la même valeur de poids et parce que nous n'avons probablement pas utilisé le même algorithme de tri. De même les seules différences dans les fichiers .p sont dans les décimales après la précision donnée.
On remarque facilement la supériorité temporelle et spatiale de la version creuse contre la version naïve, surtout lorsque N et N\_links sont grands. 
\subsection{Améliorations encore possible}
Lors de l'implémentation de la matrice compressé, nous avons choisis de compressé les lignes puisque celle-ci peuvent parfois être entièrement vide, cela est bénéfique d'un point de vu espace. Cepedant ils serait aussi intéressant de compresser G selon les colonnes puisque, bien que l'on perde en espace mémoire, on gagnerait en efficacité temporelle grâce au nombre réduit d'accès mémoire que l'on effecturait par rapport à la compression par ligne.
Lors de limplémentation de la matrice compressée, nous avons choisi de compresser les lignes puisque celle-cis peuvent parfois être entièrement vides, cela est bénéfique dun point de vue lespace. Cependant il serait aussi intéressant de compresser G selon les colonnes puisque, bien que lon perde en espace mémoire, on gagnerait en efficacité temporelle grâce au nombre réduit daccès mémoire que lon effectuerait par rapport à la compression par ligne.
Il est important de noter qu'un compression par colonne permettrait aussi de paralléliser la problème, le rendant encore plus efficace temporellement. Il est assez simple d'implémenter la parallélisation en Ada puisque cette notion fait partie à part entière du langage de programmation, mais nous ne l'avons pas implémenté par manque de temps.
Il est important de noter quune compression par colonne permettrait aussi de paralléliser le problème, le rendant encore plus efficace temporellement. Il est assez simple dimplémenter la parallélisation en Ada puisque cette notion fait partie à part entière du langage de programmation, mais nous ne lavons pas implémenté par manque de temps.
Finalement nous % todo network stack bug
Il serait aussi possible de rendre lalgorithme QuickSort générique mais nous ne lavons pas non plus fait par manque de temps.
Finalement une dernière amélioration possible est celle de supprimer network du scope lorsque nous nen navons plus besoin. En effet , une fois le réseau créé à partir du fichier .net, nous le gardons jusquà la fin de l'exécution du programme. Cependant il est possible de sen débarrasser après la création de G.
\section{Apports personnels}
Ce projet nous a permis de solidifer nos connaissances sur les structures de données, car nous avons longuement réfléchi sur lequelles étaient les plus adaptées au problème. De même nous avons eu l'occasion de revoir plusieurs algorithme classique, nous permettant de mieux les maitriser.
Ce projet nous a permis de consolider nos connaissances sur les structures de données, car nous avons longuement réfléchi lesquelles étaient les plus adaptées au problème. De même, nous avons eu loccasion de revoir plusieurs algorithmes classiques, nous permettant de mieux les maîtriser. Nous sommes tout de même déçus de ne pas avoir eu plus de temps à consacrer à loptimisation du programme.
\newpage

Binary file not shown.