écrite rapport, revert optimisation mémoire network

git-svn-id: http://cregut.svn.enseeiht.fr/2020/1sn/pim/projets/GH-05@211900 e13453a9-b01f-0410-a051-f404c4f0c485
This commit is contained in:
lfainsin 2021-01-13 14:13:57 +00:00
parent 6f990c919f
commit 8eb3244392
6 changed files with 101 additions and 63 deletions

View file

@ -47,7 +47,7 @@ Nous l'avons construit à partir de la methode des raffinages. De même, nous ut
\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 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:
\begin{itemize}
\item Une page est respectable si d'autre page la reference, et d'autant plus si elle meme sont populaire.
@ -66,17 +66,24 @@ Toute la difficulté provient de la taille massive que peut avoir le réseau. Il
\section{Architecture du programme}
Nous pouvons diviser notre programme en trois sous-parties.
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.
\subsection{Gestion des vecteurs}
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.
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. \\
Lorsque nous trierons les données nous ferons appelle à l'algorithme QuickSort
Lorsque nous trierons les données nous ferons appelle à l'algorithme QuickSort. \\
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.
\subsection{Formatage du code}
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}
TODO
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.
\subsection{Gestion du calcul du Pagerank}
@ -94,13 +101,7 @@ Notons N le nombre de pages dans le réseau, et N\_Links le nombre de liens tota
Ainsi pour stocker pi, nous choissons la structure d'un vecteur statique (array) de dimension 1xN.
\begin{adacode}
yeet
\end{adacode}
De même, pour stocker network nous choisissons la structure d'une matrice de dimension 2xN\_Links.
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).
Pour stocker G, la matrice de Google, nous avons 2 choix:
\begin{itemize}
@ -111,13 +112,13 @@ Pour stocker G, la matrice de Google, nous avons 2 choix:
\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.
L'avantage principale de cette structure est que sa construction et sa manipulation (produit vecteur/matrice) est simple.
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.
L'inconvénient est que nous stockons beaucoup de valeurs inutiles (i.e. des éléments qui se répètent ou bien qui sont égals à 0). Ainsi puisque un produit vecteur/matrice est de compléxité N\^2, nous perdons beaucoup de temps à effectuer des opérations inutiles.
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.
\subsection{Implémentation Creuse}
Il faut alors nous orienter vers une autre solution
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.
@ -127,7 +128,7 @@ 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.
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 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.
\section{Benchmark}
@ -153,9 +154,11 @@ ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
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:
\begin{bashcode}
$ grep -A 1 stack /proc/<PID>/smaps
$ 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}
\subsection{Version Naive}
\subsubsection{exemple\_sujet.net}
@ -200,7 +203,6 @@ user 0m0,016s
sys 0m0,004s
\end{bashcode}
\subsubsection{brainlinks.net}
\begin{bashcode}
$ time build/pagerank fichiers_test/Brainlinks/brainlinks.net
@ -209,7 +211,6 @@ user 1m55,909s
sys 0m0,012s
\end{bashcode}
\subsubsection{Linux26.net}
\begin{bashcode}
$ time build/pagerank fichiers_test/Linux26/Linux26.net
@ -220,6 +221,7 @@ 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.
\subsection{Améliorations encore possible}
@ -228,6 +230,8 @@ Lors de l'implémentation de la matrice compressé, nous avons choisis de compre
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.
Finalement nous % todo network stack bug
\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.

View file

@ -24,10 +24,14 @@ package Google_Creux is
rows: T_Rows;
end record;
-- permet de créer la matrice à partir du réseau
procedure create_H(mat: in out T_Google; network: in Vector_Link.T_Vecteur);
-- permet faire le produit vecteur-matrice
function calcul(vec: in Vector_Element.T_Vecteur; mat: in T_Google; alpha: in T_Element) return Vector_Element.T_Vecteur with
Pre => alpha > 0.0 and alpha < 1.0;
-- permet l'affichage de la matrice, voir l'article wikiepdia pour comprendre format
procedure put(mat: in T_Google);
end Google_Creux;

View file

@ -7,12 +7,15 @@ generic
N: Positive;
N_Links: Positive;
-- on a besoin de ce type Vector_Element
with package Vector_Natural is
new Vector.Entier(Capacite => N);
-- on a besoin de ce type pour le produit vecteur-matrice
with package Vector_Element is
new Vector.Digit(T_Digit => T_Element, Capacite => N, Vector_Entier => Vector_Natural);
-- on a besoin de ce type pour la création de H
with package Vector_Link is
new Vector.Link(Capacite => N_Links);
@ -28,11 +31,17 @@ package Google_Naive is
procedure initialize(mat: in out T_Google);
-- On place des 1 dans la matrice à partir du réseau.
procedure create_H(mat: in out T_Google; network: in Vector_Link.T_Vecteur);
-- On transforme H pour obtenir S
procedure create_S(mat: in out T_Google);
-- On transforme S pour obtenir G
procedure create_G(mat: in out T_Google; alpha: in T_Element) with
Pre => alpha > 0.0 and alpha < 1.0;
-- permet l'affiche de la matrice
procedure put(mat: in T_Google);
end Google_Naive;

View file

@ -13,7 +13,7 @@ procedure pageRank is
-- défition d'exception pour gérer le parsing des arguments.
ERROR_args: Exception;
ERROR_alpha: Exception;
ERROR_ite: Exception;
ERROR_iteration: Exception;
ERROR_filename: Exception;
INFO_tips: Exception;
INFO_help: Exception;
@ -25,6 +25,7 @@ procedure pageRank is
new Ada.Text_IO.Float_IO(Num => T_Double);
use Text_T_Double;
-- permet de faire un retour chariot pour écrire plusieurs fois sur la même ligne, pas possible avec un put
stdout: constant Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output;
@ -75,7 +76,7 @@ procedure pageRank is
elsif Argument(i) = "-i" or Argument(i) = "--ite-max" then
ite_max := Natural'Value(Argument(i+1));
if ite_max > 150 then
raise ERROR_ite;
raise ERROR_iteration;
end if;
i := i + 2;
put_line("parsed ite_max");
@ -117,7 +118,7 @@ procedure pageRank is
if Argument(i) = "-a" or Argument(i) = "--alpha" then
raise ERROR_alpha;
elsif Argument(i) = "-i" or Argument(i) = "--ite-max" then
raise ERROR_ite;
raise ERROR_iteration;
else
put_line("Unexpected constraint_error");
raise ERROR_args;
@ -164,6 +165,10 @@ begin
reset(file, In_File);
skip_line(file);
-- explication: 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.
-- on peut maintenant créer nos vecteurs
declare
package Vector_Entier is
@ -179,10 +184,42 @@ begin
use Vector_Entier;
use Vector_Link;
network: Vector_Link.T_Vecteur;
row, col: Natural;
sorted: Boolean;
dupe: Natural;
pi: Vector_Double.T_Vecteur;
pi_index: Vector_Entier.T_Vecteur;
begin
new_line;
-- on charge le réseau en mémoire
for i in 0..N_links-1 loop
get(file, row);
get(file, col);
network(i) := T_Link'(row, col);
end loop;
close(file);
put_line("loaded in memory the network");
-- on trie le réseau, si besoin
is_sorted_and_uniq(network, dupe, sorted);
if not naif then
if not sorted then
put_line("network is not sorted");
quicksort(network);
put_line("sorted the network");
end if;
if dupe > 0 then
put_line("deleted the duplicates ####### TODO #######");
end if;
end if;
new_line;
initialize(pi, 1.0/T_Double(N));
@ -215,7 +252,7 @@ begin
new_line;
create_H(G, create_network(file, N_links, naif));
create_H(G, network);
put_line("created H");
-- put(G); new_line;
@ -240,6 +277,7 @@ begin
-- put_line("final pi:");
-- put(pi); new_line;
end;
else -- not naif
declare
-- on instancie le module générique Creux de Google
@ -256,7 +294,7 @@ begin
H: T_Google;
begin
create_H(H, create_network(file, N_Links, naif));
create_H(H, network);
put_line("created H");
-- put(H); new_line; new_line;
@ -315,7 +353,7 @@ exception
put_line("Erreur lors de la saisi de alpha.");
put_line("alpha ∈ [0, 1]");
when ERROR_ite =>
when ERROR_iteration =>
new_line;
put_line("Erreur lors de la saisi de ite_max.");
put_line("ite_max ∈ ⟦0, 150⟧");

View file

@ -230,42 +230,6 @@ package body Vector is
package body Link is
function create_network(file: in out Ada.Text_IO.File_Type; N_links: in Positive; naif: in Boolean) return T_Vecteur is
network: T_Vecteur;
row, col: Natural;
sorted: Boolean;
dupe: Natural;
begin
-- on charge le réseau en mémoire
for i in 0..N_links-1 loop
get(file, row);
get(file, col);
network(i) := T_Link'(row, col);
end loop;
close(file);
put_line("loaded in memory the network");
-- on trie le réseau, si besoin
is_sorted_and_uniq(network, dupe, sorted);
if not naif then
if not sorted then
put_line("network is not sorted");
quicksort(network);
put_line("sorted the network");
end if;
if dupe > 0 then
put_line("deleted the duplicates ####### TODO #######");
end if;
end if;
return network;
end create_network;
procedure put(vec: in T_Vecteur) is
begin
for i in 0..Capacite-1 loop

View file

@ -2,6 +2,12 @@ with Ada.Text_IO; use Ada.Text_IO;
package Vector is
-- explication:
-- Nous avons choisit de créer des sous-modules simplement pour pouvoir tout regroupe dans un seul fichier
-- Nous avons aussi séparé les vecteurs d'entiers des vecteurs de flottant, car créer un seul et unique module permettant
-- la gestion d'entier ou de flottant était trop compliqué. Le code aurait été plus compliqué à lire.
-- nous avons alors choiss de séparer comme ceci le code, bien que cela produise du code un peu redondant.
generic
Capacite: Positive;
@ -10,13 +16,20 @@ package Vector is
type T_Vecteur is array (0..Capacite-1) of Natural;
-- permet d'initialiser le vecteur avec pour tout i, vec[i] = value
procedure initialize(vec: in out T_Vecteur; value: in Natural);
-- permet d'initialiser le vecteur avec pour tout i, vec[i] = i
procedure identity(vec: in out T_Vecteur);
-- permet de renverser le vecteur, équivalent vec[::-1] en python
procedure flip(vec: in out T_Vecteur);
-- procédure permettant d'afficher les vecteurs et d'écrire dans des fichiers
procedure put(vec: in T_Vecteur);
procedure put(file: in out Ada.Text_IO.File_Type; vec: in T_Vecteur);
-- procédure permettant de trier le vecteur selon l'algorithme de quicksort
procedure quicksort(vec: in out T_Vecteur; low: Natural := 0; high: Natural := Capacite-1);
end Entier;
@ -40,13 +53,18 @@ package Vector is
procedure initialize(vec: in out T_Vecteur; value: in T_Digit);
--
function sum(vec: in T_Vecteur) return T_Digit;
procedure flip(vec: in out T_Vecteur);
procedure put(vec: in T_Vecteur);
procedure put(file: in out Ada.Text_IO.File_Type; vec: in T_Vecteur);
procedure quicksort(vec: in out T_Vecteur; low: Natural := 0; high: Natural := Capacite-1);
-- procédure permettant de trier le vecteur selon l'algorithme quicksort et aussi de récupérer les indices triés.
procedure quicksort(vec: in out T_Vecteur; vec_index: in out Vector_Entier.T_Vecteur;
low: Natural := 0; high: Natural := Capacite-1);
@ -54,6 +72,9 @@ package Vector is
-- nous avons choisis de modéliser le réseau via cette structure d'un vecteur d'enregistrement plutot que d'un matrice
-- avec deux colonnes car de cette manière le trie du réseau est plus simple.
generic
Capacite: Positive;
@ -66,8 +87,6 @@ package Vector is
end record;
type T_Vecteur is array (0..Capacite-1) of T_Link;
function create_network(file: in out Ada.Text_IO.File_Type; N_links: in Positive; naif: in Boolean) return T_Vecteur;
procedure put(vec: in T_Vecteur);