Optimisation des performances en mémorisant la longueur des mots et en supprimant les parcours redondants

This commit is contained in:
rabot
2026-10-07 12:12:56 +02:00
parent 57aaec48d0
commit 1c4405c75e
5 changed files with 265 additions and 18 deletions
+20 -18
View File
@@ -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;
}
+86
View File
@@ -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
```
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+159
View File
@@ -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
<spontaneous>
[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]
-----------------------------------------------
<spontaneous>
[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