diff --git a/analyseur.c b/analyseur.c index ba4c84f..8fd5b0b 100644 --- a/analyseur.c +++ b/analyseur.c @@ -46,14 +46,6 @@ unsigned long alea(void) /* Fonctions utilitaires sur les chaînes */ /* ------------------------------------------------------------------------ */ -int longueur(const char *s) -{ - int n = 0; - while (s[n] != '\0') - n++; - return n; -} - void copier(char *dest, const char *src) { int i = 0; @@ -83,9 +75,14 @@ int comparer(const char *a, const char *b) return (unsigned char)a[i] - (unsigned char)b[i]; } -void ajouter(char *dest, const char *src) +/* position indique la fin de dest et avance avec les caractères ajoutés. */ +void ajouter(char *dest, const char *src, size_t *position) { - copier(dest + longueur(dest), src); + size_t fin = *position; + for (size_t i = 0; src[i] != '\0'; i++) + dest[fin++] = src[i]; + dest[fin] = '\0'; + *position = fin; } char en_minuscule(char c) @@ -128,9 +125,10 @@ char **generer_vocabulaire(void) for (int i = 0; i < NB_MOTS_VOCAB; i++) { vocab[i] = malloc(TAILLE_MOT); vocab[i][0] = '\0'; + size_t position = 0; int nb_syllabes = 2 + alea() % 3; for (int j = 0; j < nb_syllabes; j++) - ajouter(vocab[i], SYLLABES[alea() % NB_SYLLABES]); + ajouter(vocab[i], SYLLABES[alea() % NB_SYLLABES], &position); } return vocab; } @@ -211,7 +209,7 @@ char **decouper_lignes(const char *texte, int *nb_lignes) void normaliser_ligne(char *ligne) { - for (int i = 0; i < longueur(ligne); i++) { + for (int i = 0; ligne[i] != '\0'; i++) { if (est_lettre(ligne[i])) ligne[i] = en_minuscule(ligne[i]); else @@ -226,6 +224,7 @@ void normaliser_ligne(char *ligne) typedef struct { char mot[TAILLE_MOT]; int occurrences; + int taille; } Entree; int est_mot_vide(const char *mot) @@ -263,7 +262,8 @@ int chercher_mot(const Entree *table, const int *index_mots, const char *mot) return -(position + 1); } -void compter_mot(Entree *table, int *nb_entrees, int *index_mots, const char *mot) +void compter_mot(Entree *table, int *nb_entrees, int *index_mots, + const char *mot, int taille) { int indice = chercher_mot(table, index_mots, mot); if (indice >= 0) { @@ -278,6 +278,7 @@ void compter_mot(Entree *table, int *nb_entrees, int *index_mots, const char *mo indice = *nb_entrees; copier(table[indice].mot, mot); table[indice].occurrences = 1; + table[indice].taille = taille; index_mots[position] = indice + 1; (*nb_entrees)++; } @@ -302,7 +303,7 @@ void compter_mots_ligne(Entree *table, int *nb_entrees, int *index_mots, if (k > 0) { (*nb_mots_total)++; if (!est_mot_vide(mot)) - compter_mot(table, nb_entrees, index_mots, mot); + compter_mot(table, nb_entrees, index_mots, mot, k); } } } @@ -339,7 +340,7 @@ double longueur_moyenne(const Entree *table, int n) { long total = 0; for (int i = 0; i < n; i++) - total += longueur(table[i].mot); + total += table[i].taille; return (double)total / n; } @@ -348,7 +349,7 @@ double ecart_type_longueur(const Entree *table, int n, double moyenne) { double somme = 0.0; for (int i = 0; i < n; i++) { - double ecart = longueur(table[i].mot) - moyenne; + double ecart = table[i].taille - moyenne; somme += ecart * ecart; } return somme / n; /* variance ; la racine est prise dans main */ @@ -399,12 +400,13 @@ char *construire_rapport(const Entree *table, int n) char *rapport = malloc((size_t)n * 64 + 256); char ligne[128]; rapport[0] = '\0'; + size_t position = 0; - ajouter(rapport, "rang;mot;occurrences\n"); + ajouter(rapport, "rang;mot;occurrences\n", &position); for (int i = 0; i < n; i++) { snprintf(ligne, sizeof(ligne), "%d;%s;%d\n", i + 1, table[i].mot, table[i].occurrences); - ajouter(rapport, ligne); + ajouter(rapport, ligne, &position); } return rapport; } diff --git a/notes.md b/notes.md index 60a6664..d34bfd2 100644 --- a/notes.md +++ b/notes.md @@ -280,3 +280,89 @@ mkdir -p out/apres_hachage gcc -O0 -pg analyseur.c -o out/apres_hachage/analyseur (cd out/apres_hachage && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_hachage.txt) ``` + +## Longueurs mémorisées et suppression des parcours répétés + +Le champ `taille` de `Entree` mémorise la longueur du mot à son insertion. +La valeur vient directement de `k`, déjà calculé dans `compter_mots_ligne` : +aucun parcours supplémentaire n'est nécessaire. La moyenne et la variance +lisent désormais ce champ. Le tri déplace l'entrée entière, donc la longueur +reste associée au bon mot. Pour un mot tronqué, la taille enregistrée est +celle du mot conservé, soit au maximum 31 caractères. + +Deux autres changements éliminent les recalculs sur les chaînes longues : + +- `normaliser_ligne` s'arrête directement sur `ligne[i] == '\0'`. Le parcours + d'une ligne de L caractères passe de O(L²) à O(L). +- `ajouter` reçoit une position de fin, qu'il avance pendant la copie des + caractères ajoutés. Cette position est conservée lors de la construction + du rapport et réinitialisée pour chaque mot du vocabulaire. Le préfixe + déjà construit n'est plus parcouru à chaque ajout. + +La fonction `longueur`, devenue inutilisée, a été supprimée. +Le champ supplémentaire fait passer `sizeof(Entree)` de 36 à 40 octets sur +cette machine : le tableau alloué pour 20 000 entrées prend 80 000 octets +supplémentaires, soit environ 78 Kio. + +Profil enregistré dans [out/profil_apres_longueurs.txt](out/profil_apres_longueurs.txt). +La version précédente, avec la table de hachage, est décrite dans +`out/profil_apres_hachage.txt`. + +| Mesure | Avant | Après | +| --- | ---: | ---: | +| Appels à `longueur()` depuis les statistiques | 11 732 | 0 | +| Appels à `longueur()` depuis `normaliser_ligne` | 1 561 330 | 0 | +| Appels à `longueur()` depuis `ajouter` | 26 801 | 0 | +| Appels à `longueur()` au total | 1 599 863 | 0 | +| Temps réel médian sans profiling (`gcc -O0`, 5 exécutions par version) | 0,832 s | 0,362 s | + +Le temps total baisse de 56,5 %, soit une accélération d'environ 2,30. +Cette mesure porte sur l'ensemble des trois changements, et ne doit pas +être attribuée uniquement au stockage de la longueur des mots. + +Les cinq mesures, réalisées en alternant les deux versions sur la même +machine, sont respectivement : 0,835 / 0,838 / 0,830 / 0,832 / 0,829 s +avant, et 0,362 / 0,369 / 0,366 / 0,362 / 0,361 s après. + +Validation : compilation sans avertissement avec `-Wall -Wextra -Wpedantic`, +rapport et sortie console identiques octet par octet sur les dix exécutions, +avec `-pg` et sous AddressSanitizer et UndefinedBehaviorSanitizer. +L'empreinte reste **`1d53de903efd2b7c`**, la moyenne `6.712` et l'écart-type +`1.873`. Des tests temporaires ont également vérifié les ajouts vides et +successifs jusqu'à la limite d'un tampon, la normalisation, les longueurs +avant/après tri, les statistiques, le rapport vide et les mots tronqués. + +Commandes pour reproduire ce profil : + +```bash +mkdir -p out/apres_longueurs +gcc -O0 -pg analyseur.c -o out/apres_longueurs/analyseur +(cd out/apres_longueurs && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_longueurs.txt) +``` + +```bash +[rabot@salle235-06 out]$ time ./analyseur +Lignes analysées : 21746 +Mots au total : 250000 +Mots distincts : 5866 +Longueur moyenne : 6.712 (écart-type 1.873) +Bigramme le + courant : "me" (17607 fois, souvent suivi de 'n') + +Top 10 : + 1. rane 287 + 2. fasi 241 + 3. tata 219 + 4. mopo 214 + 5. cofi 180 + 6. vesi 179 + 7. laganomo 177 + 8. boto 172 + 9. nopebo 169 + 10. rotion 165 + +Empreinte du rapport : 1d53de903efd2b7c + +real 0m0.751s +user 0m0.703s +sys 0m0.004s +``` \ No newline at end of file diff --git a/out/analyseur b/out/analyseur index 5d9206f..65c902b 100755 Binary files a/out/analyseur and b/out/analyseur differ diff --git a/out/gmon.out b/out/gmon.out index 03ef1ab..ac86df0 100644 Binary files a/out/gmon.out and b/out/gmon.out differ diff --git a/out/profil_apres_longueurs.txt b/out/profil_apres_longueurs.txt new file mode 100644 index 0000000..d5f57dd --- /dev/null +++ b/out/profil_apres_longueurs.txt @@ -0,0 +1,159 @@ +Flat profile: + +Each sample counts as 0.01 seconds. + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 22.22 0.06 0.06 250000 0.00 0.00 est_mot_vide + 18.52 0.11 0.05 17202045 0.00 0.00 doit_echanger + 12.96 0.14 0.04 12172087 0.00 0.00 comparer + 11.11 0.17 0.03 5297256 0.00 0.00 est_lettre + 11.11 0.20 0.03 _init + 9.26 0.23 0.03 12198772 0.00 0.00 egales + 7.41 0.25 0.02 1 20.00 105.00 trier_entrees + 3.70 0.26 0.01 21746 0.00 0.00 normaliser_ligne + 1.85 0.27 0.01 150182 0.00 0.00 chercher_mot + 1.85 0.27 0.01 150182 0.00 0.00 hacher_mot + 0.00 0.27 0.00 1266663 0.00 0.00 en_minuscule + 0.00 0.27 0.00 928117 0.00 0.00 alea + 0.00 0.27 0.00 150182 0.00 0.00 compter_mot + 0.00 0.27 0.00 26801 0.00 0.00 ajouter + 0.00 0.27 0.00 21746 0.00 0.00 compter_mots_ligne + 0.00 0.27 0.00 5866 0.00 0.00 copier + 0.00 0.27 0.00 1 0.00 21.28 analyser_bigrammes + 0.00 0.27 0.00 1 0.00 0.00 construire_rapport + 0.00 0.27 0.00 1 0.00 0.00 decouper_lignes + 0.00 0.27 0.00 1 0.00 0.00 ecart_type_longueur + 0.00 0.27 0.00 1 0.00 0.00 empreinte + 0.00 0.27 0.00 1 0.00 0.00 generer_texte + 0.00 0.27 0.00 1 0.00 0.00 generer_vocabulaire + 0.00 0.27 0.00 1 0.00 0.00 longueur_moyenne + 0.00 0.27 0.00 1 0.00 0.00 racine + + Call graph + + +granularity: each sample hit covers 4 byte(s) for 3.70% of 0.27 seconds + +index % time self children called name + +[1] 88.9 0.00 0.24 main [1] + 0.02 0.09 1/1 trier_entrees [2] + 0.00 0.10 21746/21746 compter_mots_ligne [3] + 0.00 0.02 1/1 analyser_bigrammes [10] + 0.01 0.01 21746/21746 normaliser_ligne [11] + 0.00 0.00 1/1 generer_vocabulaire [24] + 0.00 0.00 1/1 generer_texte [23] + 0.00 0.00 1/1 decouper_lignes [20] + 0.00 0.00 1/1 longueur_moyenne [25] + 0.00 0.00 1/1 ecart_type_longueur [21] + 0.00 0.00 1/1 racine [26] + 0.00 0.00 1/1 construire_rapport [19] + 0.00 0.00 1/1 empreinte [22] +----------------------------------------------- + 0.02 0.09 1/1 main [1] +[2] 38.9 0.02 0.09 1 trier_entrees [2] + 0.05 0.04 17202045/17202045 doit_echanger [4] +----------------------------------------------- + 0.00 0.10 21746/21746 main [1] +[3] 35.2 0.00 0.10 21746 compter_mots_ligne [3] + 0.06 0.02 250000/250000 est_mot_vide [5] + 0.00 0.01 150182/150182 compter_mot [13] +----------------------------------------------- + 0.05 0.04 17202045/17202045 trier_entrees [2] +[4] 31.5 0.05 0.04 17202045 doit_echanger [4] + 0.04 0.00 12172087/12172087 comparer [6] +----------------------------------------------- + 0.06 0.02 250000/250000 compter_mots_ligne [3] +[5] 31.4 0.06 0.02 250000 est_mot_vide [5] + 0.02 0.00 12045671/12198772 egales [9] +----------------------------------------------- + 0.04 0.00 12172087/12172087 doit_echanger [4] +[6] 13.0 0.04 0.00 12172087 comparer [6] +----------------------------------------------- + 0.01 0.00 1539584/5297256 normaliser_ligne [11] + 0.02 0.00 3757672/5297256 analyser_bigrammes [10] +[7] 11.1 0.03 0.00 5297256 est_lettre [7] +----------------------------------------------- + +[8] 11.1 0.03 0.00 _init [8] +----------------------------------------------- + 0.00 0.00 153101/12198772 chercher_mot [12] + 0.02 0.00 12045671/12198772 est_mot_vide [5] +[9] 9.3 0.03 0.00 12198772 egales [9] +----------------------------------------------- + 0.00 0.02 1/1 main [1] +[10] 7.9 0.00 0.02 1 analyser_bigrammes [10] + 0.02 0.00 3757672/5297256 est_lettre [7] +----------------------------------------------- + 0.01 0.01 21746/21746 main [1] +[11] 6.9 0.01 0.01 21746 normaliser_ligne [11] + 0.01 0.00 1539584/5297256 est_lettre [7] + 0.00 0.00 1266663/1266663 en_minuscule [15] +----------------------------------------------- + 0.01 0.01 150182/150182 compter_mot [13] +[12] 3.8 0.01 0.01 150182 chercher_mot [12] + 0.01 0.00 150182/150182 hacher_mot [14] + 0.00 0.00 153101/12198772 egales [9] +----------------------------------------------- + 0.00 0.01 150182/150182 compter_mots_ligne [3] +[13] 3.8 0.00 0.01 150182 compter_mot [13] + 0.01 0.01 150182/150182 chercher_mot [12] + 0.00 0.00 5866/5866 copier [18] +----------------------------------------------- + 0.01 0.00 150182/150182 chercher_mot [12] +[14] 1.9 0.01 0.00 150182 hacher_mot [14] +----------------------------------------------- + 0.00 0.00 1266663/1266663 normaliser_ligne [11] +[15] 0.0 0.00 0.00 1266663 en_minuscule [15] +----------------------------------------------- + 0.00 0.00 27934/928117 generer_vocabulaire [24] + 0.00 0.00 900183/928117 generer_texte [23] +[16] 0.0 0.00 0.00 928117 alea [16] +----------------------------------------------- + 0.00 0.00 5867/26801 construire_rapport [19] + 0.00 0.00 20934/26801 generer_vocabulaire [24] +[17] 0.0 0.00 0.00 26801 ajouter [17] +----------------------------------------------- + 0.00 0.00 5866/5866 compter_mot [13] +[18] 0.0 0.00 0.00 5866 copier [18] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[19] 0.0 0.00 0.00 1 construire_rapport [19] + 0.00 0.00 5867/26801 ajouter [17] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[20] 0.0 0.00 0.00 1 decouper_lignes [20] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[21] 0.0 0.00 0.00 1 ecart_type_longueur [21] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[22] 0.0 0.00 0.00 1 empreinte [22] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[23] 0.0 0.00 0.00 1 generer_texte [23] + 0.00 0.00 900183/928117 alea [16] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[24] 0.0 0.00 0.00 1 generer_vocabulaire [24] + 0.00 0.00 27934/928117 alea [16] + 0.00 0.00 20934/26801 ajouter [17] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[25] 0.0 0.00 0.00 1 longueur_moyenne [25] +----------------------------------------------- + 0.00 0.00 1/1 main [1] +[26] 0.0 0.00 0.00 1 racine [26] +----------------------------------------------- + +Index by function name + + [8] _init [18] copier [23] generer_texte + [17] ajouter [20] decouper_lignes [24] generer_vocabulaire + [16] alea [4] doit_echanger [14] hacher_mot + [10] analyser_bigrammes [21] ecart_type_longueur [25] longueur_moyenne + [12] chercher_mot [9] egales [11] normaliser_ligne + [6] comparer [22] empreinte [26] racine + [13] compter_mot [15] en_minuscule [2] trier_entrees + [3] compter_mots_ligne [7] est_lettre + [19] construire_rapport [5] est_mot_vide