Optimisé chercher_mot par recherche dichotomique

This commit is contained in:
rabot
2026-10-07 11:30:14 +02:00
parent 55320bdfea
commit be387e2066
7 changed files with 6381 additions and 6 deletions
+21 -6
View File
@@ -235,13 +235,24 @@ int est_mot_vide(const char *mot)
return 0;
}
/* Le tableau reste trié alphabétiquement pendant le comptage.
* Renvoie l'indice du mot, ou -(position d'insertion + 1) s'il est absent. */
int chercher_mot(Entree *table, int nb_entrees, const char *mot)
{
for (int i = 0; i < nb_entrees; i++) {
if (egales(table[i].mot, mot))
return i;
int debut = 0;
int fin = nb_entrees - 1;
while (debut <= fin) {
int milieu = debut + (fin - debut) / 2;
int comparaison = comparer(table[milieu].mot, mot);
if (comparaison == 0)
return milieu;
if (comparaison < 0)
debut = milieu + 1;
else
fin = milieu - 1;
}
return -1;
return -(debut + 1);
}
void compter_mot(Entree *table, int *nb_entrees, const char *mot)
@@ -250,8 +261,12 @@ void compter_mot(Entree *table, int *nb_entrees, const char *mot)
if (indice >= 0) {
table[indice].occurrences++;
} else {
copier(table[*nb_entrees].mot, mot);
table[*nb_entrees].occurrences = 1;
indice = -indice - 1;
/* Décaler uniquement lors de la première apparition d'un mot. */
for (int i = *nb_entrees; i > indice; i--)
table[i] = table[i - 1];
copier(table[indice].mot, mot);
table[indice].occurrences = 1;
(*nb_entrees)++;
}
}
+155
View File
@@ -0,0 +1,155 @@
Lancement du programme après la compilation avec `gcc -O0 analyseur.c -o analyseur`
```bash
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
```
Et ensuite la commande time sur l'exécutable avec `time ./analyseur `
```bash
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 0m2.858s
user 0m2.821s
sys 0m0.013s
```
On recompile avec le flag `-pg` : `gcc -O0 -pg analyseur.c -o analyseur`
Et on relance `time` dessus
```bash
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 0m6.354s
user 0m6.295s
sys 0m0.020s
```
On lance `gprof` : `gprof -b ./analyseur gmon.out > profil_avant.txt`
## Optimisation de `chercher_mot` : recherche dichotomique
La recherche initiale parcourait toutes les entrées jusqu'à trouver le mot.
Pour 150 182 recherches, elle effectuait 296 656 203 appels à `egales()`.
Le tableau est maintenant maintenu dans l'ordre alphabétique pendant le
comptage. `chercher_mot` utilise `comparer()` pour éliminer la moitié des
entrées restantes à chaque étape : la recherche passe de O(n) à O(log n).
Si le mot est absent, la fonction renvoie `-(position d'insertion + 1)` ;
`compter_mot` retrouve cette position avec `-indice - 1`, décale les entrées
et insère le nouveau mot. Cette insertion reste en O(n), mais elle n'a lieu
qu'à la première apparition de chacun des 5 866 mots distincts.
Le tableau existant est réutilisé, sans allocation supplémentaire.
Le tri final par fréquence produit le même classement.
Résultats du nouveau profil, conservé dans [out/profil_apres.txt](out/profil_apres.txt) :
| Mesure | Avant | Après |
| --- | ---: | ---: |
| Appels de `chercher_mot` | 150 182 | 150 182 |
| Appels à `egales()` depuis `chercher_mot` | 296 656 203 | 0 |
| Comparaisons de chaînes dans `chercher_mot` | 296 656 203 | 1 718 447 |
| Appels à `egales()` dans tout le programme | 308 701 874 | 12 045 671 |
| Temps réel sans profiling (`gcc -O0`) | 2,80 s | 1,45 s |
Les recherches effectuent environ 173 fois moins de comparaisons.
Les appels restants à `egales()` viennent de `est_mot_vide`.
Les temps ci-dessus correspondent à une mesure avant/après sur la même
machine pendant cette modification : environ 48 % de temps en moins
(accélération d'environ 1,93). Ce sont des mesures ponctuelles.
Validation : compilation avec `-Wall -Wextra -Wpedantic`, rapport et sortie
console identiques octet par octet à la version initiale, avec et sans `-pg`.
L'empreinte reste **`1d53de903efd2b7c`**.
Un test temporaire sous AddressSanitizer et UndefinedBehaviorSanitizer a
également vérifié les recherches dans un tableau vide, les mots présents
ou absents, les insertions au début/milieu/fin, les doublons et les mots de
31 caractères, puis comparé 10 000 comptages à une référence linéaire.
Pour reproduire le profil sans écraser les fichiers du profil initial :
```bash
mkdir -p out/apres
gcc -O0 -pg analyseur.c -o out/apres/analyseur
(cd out/apres && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres.txt)
```
```bash
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 0m1.986s
user 0m1.934s
sys 0m0.011s
```
Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+169
View File
@@ -0,0 +1,169 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
64.58 0.78 0.78 36009819 0.00 0.00 longueur
9.17 0.89 0.11 5867 0.02 0.14 longueur_moyenne
7.50 0.97 0.09 1 90.00 139.54 trier_entrees
5.00 1.03 0.06 9860216 0.00 0.00 comparer
2.92 1.07 0.04 32667 0.00 0.00 copier
2.50 1.10 0.03 12045671 0.00 0.00 egales
2.50 1.13 0.03 _init
1.67 1.15 0.02 250000 0.00 0.00 est_mot_vide
1.67 1.17 0.02 150182 0.00 0.00 chercher_mot
1.67 1.19 0.02 150182 0.00 0.00 compter_mot
0.83 1.20 0.01 1 10.00 10.00 decouper_lignes
0.00 1.20 0.00 17202045 0.00 0.00 doit_echanger
0.00 1.20 0.00 5297256 0.00 0.00 est_lettre
0.00 1.20 0.00 1266663 0.00 0.00 en_minuscule
0.00 1.20 0.00 928117 0.00 0.00 alea
0.00 1.20 0.00 26801 0.00 0.00 ajouter
0.00 1.20 0.00 21746 0.00 0.00 compter_mots_ligne
0.00 1.20 0.00 21746 0.00 0.00 normaliser_ligne
0.00 1.20 0.00 1 0.00 0.00 analyser_bigrammes
0.00 1.20 0.00 1 0.00 6.41 construire_rapport
0.00 1.20 0.00 1 0.00 850.68 ecart_type_longueur
0.00 1.20 0.00 1 0.00 0.00 empreinte
0.00 1.20 0.00 1 0.00 0.00 generer_texte
0.00 1.20 0.00 1 0.00 22.88 generer_vocabulaire
0.00 1.20 0.00 1 0.00 0.00 racine
Call graph
granularity: each sample hit covers 4 byte(s) for 0.83% of 1.20 seconds
index % time self children called name
<spontaneous>
[1] 97.5 0.00 1.17 main [1]
0.00 0.85 1/1 ecart_type_longueur [3]
0.09 0.05 1/1 trier_entrees [5]
0.00 0.11 21746/21746 compter_mots_ligne [6]
0.00 0.03 21746/21746 normaliser_ligne [12]
0.00 0.02 1/1 generer_vocabulaire [17]
0.01 0.00 1/1 decouper_lignes [18]
0.00 0.01 1/1 construire_rapport [19]
0.00 0.00 1/5867 longueur_moyenne [2]
0.00 0.00 1/1 generer_texte [25]
0.00 0.00 1/1 racine [26]
0.00 0.00 1/1 analyser_bigrammes [23]
0.00 0.00 1/1 empreinte [24]
-----------------------------------------------
0.00 0.00 1/5867 main [1]
0.11 0.74 5866/5867 ecart_type_longueur [3]
[2] 70.9 0.11 0.74 5867 longueur_moyenne [2]
0.74 0.00 34415822/36009819 longueur [4]
-----------------------------------------------
0.00 0.85 1/1 main [1]
[3] 70.9 0.00 0.85 1 ecart_type_longueur [3]
0.11 0.74 5866/5867 longueur_moyenne [2]
0.00 0.00 5866/36009819 longueur [4]
-----------------------------------------------
0.00 0.00 5866/36009819 ecart_type_longueur [3]
0.00 0.00 26801/36009819 ajouter [16]
0.03 0.00 1561330/36009819 normaliser_ligne [12]
0.74 0.00 34415822/36009819 longueur_moyenne [2]
[4] 64.6 0.78 0.00 36009819 longueur [4]
-----------------------------------------------
0.09 0.05 1/1 main [1]
[5] 11.6 0.09 0.05 1 trier_entrees [5]
0.00 0.05 17202045/17202045 doit_echanger [10]
-----------------------------------------------
0.00 0.11 21746/21746 main [1]
[6] 8.9 0.00 0.11 21746 compter_mots_ligne [6]
0.02 0.04 150182/150182 compter_mot [8]
0.02 0.03 250000/250000 est_mot_vide [9]
-----------------------------------------------
0.01 0.00 1718447/9860216 chercher_mot [13]
0.05 0.00 8141769/9860216 doit_echanger [10]
[7] 5.0 0.06 0.00 9860216 comparer [7]
-----------------------------------------------
0.02 0.04 150182/150182 compter_mots_ligne [6]
[8] 4.7 0.02 0.04 150182 compter_mot [8]
0.02 0.01 150182/150182 chercher_mot [13]
0.01 0.00 5866/32667 copier [11]
-----------------------------------------------
0.02 0.03 250000/250000 compter_mots_ligne [6]
[9] 4.2 0.02 0.03 250000 est_mot_vide [9]
0.03 0.00 12045671/12045671 egales [14]
-----------------------------------------------
0.00 0.05 17202045/17202045 trier_entrees [5]
[10] 4.1 0.00 0.05 17202045 doit_echanger [10]
0.05 0.00 8141769/9860216 comparer [7]
-----------------------------------------------
0.01 0.00 5866/32667 compter_mot [8]
0.03 0.00 26801/32667 ajouter [16]
[11] 2.9 0.04 0.00 32667 copier [11]
-----------------------------------------------
0.00 0.03 21746/21746 main [1]
[12] 2.8 0.00 0.03 21746 normaliser_ligne [12]
0.03 0.00 1561330/36009819 longueur [4]
0.00 0.00 1539584/5297256 est_lettre [20]
0.00 0.00 1266663/1266663 en_minuscule [21]
-----------------------------------------------
0.02 0.01 150182/150182 compter_mot [8]
[13] 2.5 0.02 0.01 150182 chercher_mot [13]
0.01 0.00 1718447/9860216 comparer [7]
-----------------------------------------------
0.03 0.00 12045671/12045671 est_mot_vide [9]
[14] 2.5 0.03 0.00 12045671 egales [14]
-----------------------------------------------
<spontaneous>
[15] 2.5 0.03 0.00 _init [15]
-----------------------------------------------
0.00 0.01 5867/26801 construire_rapport [19]
0.00 0.02 20934/26801 generer_vocabulaire [17]
[16] 2.4 0.00 0.03 26801 ajouter [16]
0.03 0.00 26801/32667 copier [11]
0.00 0.00 26801/36009819 longueur [4]
-----------------------------------------------
0.00 0.02 1/1 main [1]
[17] 1.9 0.00 0.02 1 generer_vocabulaire [17]
0.00 0.02 20934/26801 ajouter [16]
0.00 0.00 27934/928117 alea [22]
-----------------------------------------------
0.01 0.00 1/1 main [1]
[18] 0.8 0.01 0.00 1 decouper_lignes [18]
-----------------------------------------------
0.00 0.01 1/1 main [1]
[19] 0.5 0.00 0.01 1 construire_rapport [19]
0.00 0.01 5867/26801 ajouter [16]
-----------------------------------------------
0.00 0.00 1539584/5297256 normaliser_ligne [12]
0.00 0.00 3757672/5297256 analyser_bigrammes [23]
[20] 0.0 0.00 0.00 5297256 est_lettre [20]
-----------------------------------------------
0.00 0.00 1266663/1266663 normaliser_ligne [12]
[21] 0.0 0.00 0.00 1266663 en_minuscule [21]
-----------------------------------------------
0.00 0.00 27934/928117 generer_vocabulaire [17]
0.00 0.00 900183/928117 generer_texte [25]
[22] 0.0 0.00 0.00 928117 alea [22]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[23] 0.0 0.00 0.00 1 analyser_bigrammes [23]
0.00 0.00 3757672/5297256 est_lettre [20]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[24] 0.0 0.00 0.00 1 empreinte [24]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[25] 0.0 0.00 0.00 1 generer_texte [25]
0.00 0.00 900183/928117 alea [22]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[26] 0.0 0.00 0.00 1 racine [26]
-----------------------------------------------
Index by function name
[15] _init [11] copier [25] generer_texte
[16] ajouter [18] decouper_lignes [17] generer_vocabulaire
[22] alea [10] doit_echanger [4] longueur
[23] analyser_bigrammes [3] ecart_type_longueur [2] longueur_moyenne
[13] chercher_mot [14] egales [12] normaliser_ligne
[7] comparer [24] empreinte [26] racine
[8] compter_mot [21] en_minuscule [5] trier_entrees
[6] compter_mots_ligne [20] est_lettre
[19] construire_rapport [9] est_mot_vide
+169
View File
@@ -0,0 +1,169 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
36.36 0.76 0.76 36 009 819 0.00 0.00 longueur
26.32 1.31 0.55 308 701 874 0.00 0.00 egales
18.18 1.69 0.38 150 182 0.00 0.01 chercher_mot
5.26 1.80 0.11 1 110.00 180.00 trier_entrees
3.35 1.87 0.07 12 172 087 0.00 0.00 comparer
3.35 1.94 0.07 5867 0.01 0.14 longueur_moyenne
1.91 1.98 0.04 _init
1.44 2.01 0.03 32 667 0.00 0.00 copier
0.96 2.03 0.02 250 000 0.00 0.00 est_mot_vide
0.48 2.04 0.01 5 297 256 0.00 0.00 est_lettre
0.48 2.05 0.01 928 117 0.00 0.00 alea
0.48 2.06 0.01 150 182 0.00 0.01 compter_mot
0.48 2.07 0.01 21 746 0.00 0.04 compter_mots_ligne
0.48 2.08 0.01 21 746 0.00 0.00 normaliser_ligne
0.48 2.09 0.01 1 10.00 17.09 analyser_bigrammes
0.00 2.09 0.00 17 202 045 0.00 0.00 doit_echanger
0.00 2.09 0.00 1 266 663 0.00 0.00 en_minuscule
0.00 2.09 0.00 26 801 0.00 0.00 ajouter
0.00 2.09 0.00 1 0.00 5.51 construire_rapport
0.00 2.09 0.00 1 0.00 0.00 decouper_lignes
0.00 2.09 0.00 1 0.00 796.35 ecart_type_longueur
0.00 2.09 0.00 1 0.00 0.00 empreinte
0.00 2.09 0.00 1 0.00 9.70 generer_texte
0.00 2.09 0.00 1 0.00 19.97 generer_vocabulaire
0.00 2.09 0.00 1 0.00 0.00 racine
Call graph
granularity: each sample hit covers 4 byte(s) for 0.48% of 2.09 seconds
index % time self children called name
<spontaneous>
[1] 98.1 0.00 2.05 main [1]
0.01 0.97 21746/21746 compter_mots_ligne [2]
0.00 0.80 1/1 ecart_type_longueur [6]
0.11 0.07 1/1 trier_entrees [9]
0.01 0.04 21746/21746 normaliser_ligne [12]
0.00 0.02 1/1 generer_vocabulaire [17]
0.01 0.01 1/1 analyser_bigrammes [18]
0.00 0.01 1/1 generer_texte [21]
0.00 0.01 1/1 construire_rapport [22]
0.00 0.00 1/5867 longueur_moyenne [5]
0.00 0.00 1/1 decouper_lignes [24]
0.00 0.00 1/1 racine [26]
0.00 0.00 1/1 empreinte [25]
-----------------------------------------------
0.01 0.97 21746/21746 main [1]
[2] 46.7 0.01 0.97 21746 compter_mots_ligne [2]
0.01 0.91 150182/150182 compter_mot [3]
0.02 0.02 250000/250000 est_mot_vide [13]
-----------------------------------------------
0.01 0.91 150182/150182 compter_mots_ligne [2]
[3] 44.2 0.01 0.91 150182 compter_mot [3]
0.38 0.53 150182/150182 chercher_mot [4]
0.01 0.00 5866/32667 copier [15]
-----------------------------------------------
0.38 0.53 150182/150182 compter_mot [3]
[4] 43.5 0.38 0.53 150182 chercher_mot [4]
0.53 0.00 296656203/308701874 egales [8]
-----------------------------------------------
0.00 0.00 1/5867 main [1]
0.07 0.73 5866/5867 ecart_type_longueur [6]
[5] 38.1 0.07 0.73 5867 longueur_moyenne [5]
0.73 0.00 34415822/36009819 longueur [7]
-----------------------------------------------
0.00 0.80 1/1 main [1]
[6] 38.1 0.00 0.80 1 ecart_type_longueur [6]
0.07 0.73 5866/5867 longueur_moyenne [5]
0.00 0.00 5866/36009819 longueur [7]
-----------------------------------------------
0.00 0.00 5866/36009819 ecart_type_longueur [6]
0.00 0.00 26801/36009819 ajouter [16]
0.03 0.00 1561330/36009819 normaliser_ligne [12]
0.73 0.00 34415822/36009819 longueur_moyenne [5]
[7] 36.4 0.76 0.00 36009819 longueur [7]
-----------------------------------------------
0.02 0.00 12045671/308701874 est_mot_vide [13]
0.53 0.00 296656203/308701874 chercher_mot [4]
[8] 26.3 0.55 0.00 308701874 egales [8]
-----------------------------------------------
0.11 0.07 1/1 main [1]
[9] 8.6 0.11 0.07 1 trier_entrees [9]
0.00 0.07 17202045/17202045 doit_echanger [10]
-----------------------------------------------
0.00 0.07 17202045/17202045 trier_entrees [9]
[10] 3.3 0.00 0.07 17202045 doit_echanger [10]
0.07 0.00 12172087/12172087 comparer [11]
-----------------------------------------------
0.07 0.00 12172087/12172087 doit_echanger [10]
[11] 3.3 0.07 0.00 12172087 comparer [11]
-----------------------------------------------
0.01 0.04 21746/21746 main [1]
[12] 2.2 0.01 0.04 21746 normaliser_ligne [12]
0.03 0.00 1561330/36009819 longueur [7]
0.00 0.00 1539584/5297256 est_lettre [19]
0.00 0.00 1266663/1266663 en_minuscule [23]
-----------------------------------------------
0.02 0.02 250000/250000 compter_mots_ligne [2]
[13] 2.0 0.02 0.02 250000 est_mot_vide [13]
0.02 0.00 12045671/308701874 egales [8]
-----------------------------------------------
<spontaneous>
[14] 1.9 0.04 0.00 _init [14]
-----------------------------------------------
0.01 0.00 5866/32667 compter_mot [3]
0.02 0.00 26801/32667 ajouter [16]
[15] 1.4 0.03 0.00 32667 copier [15]
-----------------------------------------------
0.00 0.01 5867/26801 construire_rapport [22]
0.00 0.02 20934/26801 generer_vocabulaire [17]
[16] 1.2 0.00 0.03 26801 ajouter [16]
0.02 0.00 26801/32667 copier [15]
0.00 0.00 26801/36009819 longueur [7]
-----------------------------------------------
0.00 0.02 1/1 main [1]
[17] 1.0 0.00 0.02 1 generer_vocabulaire [17]
0.00 0.02 20934/26801 ajouter [16]
0.00 0.00 27934/928117 alea [20]
-----------------------------------------------
0.01 0.01 1/1 main [1]
[18] 0.8 0.01 0.01 1 analyser_bigrammes [18]
0.01 0.00 3757672/5297256 est_lettre [19]
-----------------------------------------------
0.00 0.00 1539584/5297256 normaliser_ligne [12]
0.01 0.00 3757672/5297256 analyser_bigrammes [18]
[19] 0.5 0.01 0.00 5297256 est_lettre [19]
-----------------------------------------------
0.00 0.00 27934/928117 generer_vocabulaire [17]
0.01 0.00 900183/928117 generer_texte [21]
[20] 0.5 0.01 0.00 928117 alea [20]
-----------------------------------------------
0.00 0.01 1/1 main [1]
[21] 0.5 0.00 0.01 1 generer_texte [21]
0.01 0.00 900183/928117 alea [20]
-----------------------------------------------
0.00 0.01 1/1 main [1]
[22] 0.3 0.00 0.01 1 construire_rapport [22]
0.00 0.01 5867/26801 ajouter [16]
-----------------------------------------------
0.00 0.00 1266663/1266663 normaliser_ligne [12]
[23] 0.0 0.00 0.00 1266663 en_minuscule [23]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[24] 0.0 0.00 0.00 1 decouper_lignes [24]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[25] 0.0 0.00 0.00 1 empreinte [25]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[26] 0.0 0.00 0.00 1 racine [26]
-----------------------------------------------
Index by function name
[14] _init [15] copier [21] generer_texte
[16] ajouter [24] decouper_lignes [17] generer_vocabulaire
[20] alea [10] doit_echanger [7] longueur
[18] analyser_bigrammes [6] ecart_type_longueur [5] longueur_moyenne
[4] chercher_mot [8] egales [12] normaliser_ligne
[11] comparer [25] empreinte [26] racine
[3] compter_mot [23] en_minuscule [9] trier_entrees
[2] compter_mots_ligne [19] est_lettre
[22] construire_rapport [13] est_mot_vide
+5867
View File
File diff suppressed because it is too large Load Diff