From 8eb3244392c129af30f93b779b0054521fb73503 Mon Sep 17 00:00:00 2001 From: lfainsin Date: Wed, 13 Jan 2021 14:13:57 +0000 Subject: [PATCH] =?UTF-8?q?=C3=A9crite=20rapport,=20revert=20optimisation?= =?UTF-8?q?=20m=C3=A9moire=20network?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://cregut.svn.enseeiht.fr/2020/1sn/pim/projets/GH-05@211900 e13453a9-b01f-0410-a051-f404c4f0c485 --- doc/rapport.tex | 42 ++++++++++++++++++++----------------- src/google_creux.ads | 4 ++++ src/google_naive.ads | 9 ++++++++ src/pagerank.adb | 50 ++++++++++++++++++++++++++++++++++++++------ src/vector.adb | 36 ------------------------------- src/vector.ads | 23 ++++++++++++++++++-- 6 files changed, 101 insertions(+), 63 deletions(-) diff --git a/doc/rapport.tex b/doc/rapport.tex index 6ebd71a..b63c996 100644 --- a/doc/rapport.tex +++ b/doc/rapport.tex @@ -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//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. diff --git a/src/google_creux.ads b/src/google_creux.ads index b756c2b..ccf07c1 100644 --- a/src/google_creux.ads +++ b/src/google_creux.ads @@ -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; diff --git a/src/google_naive.ads b/src/google_naive.ads index f011275..8d802b0 100644 --- a/src/google_naive.ads +++ b/src/google_naive.ads @@ -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; diff --git a/src/pagerank.adb b/src/pagerank.adb index 508e5d3..d8bbb71 100644 --- a/src/pagerank.adb +++ b/src/pagerank.adb @@ -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⟧"); diff --git a/src/vector.adb b/src/vector.adb index 0d64b72..8a25664 100644 --- a/src/vector.adb +++ b/src/vector.adb @@ -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 diff --git a/src/vector.ads b/src/vector.ads index c7ff58a..1f6b9ee 100644 --- a/src/vector.ads +++ b/src/vector.ads @@ -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);