2026-10-07 11:30:14 +02:00
|
|
|
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
|
2026-10-07 11:39:55 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Optimisation des appels à `longueur_moyenne`
|
|
|
|
|
|
|
|
|
|
`longueur_moyenne` parcourt les mots une fois par appel. Le coût excessif
|
|
|
|
|
venait de `ecart_type_longueur`, qui recalculait cette même moyenne à
|
|
|
|
|
chaque itération, soit 5 866 fois. Avec l'appel de `main`, cela représentait
|
|
|
|
|
5 867 calculs de moyenne et 34 415 822 appels à `longueur()`.
|
|
|
|
|
|
|
|
|
|
`main` calcule désormais la moyenne une seule fois et la transmet en
|
|
|
|
|
paramètre à `ecart_type_longueur`. La boucle utilise directement cette
|
|
|
|
|
valeur. Le calcul des statistiques passe ainsi de O(n²) à O(n), sans
|
|
|
|
|
allocation supplémentaire ni modification de la formule de variance.
|
|
|
|
|
|
|
|
|
|
Profil enregistré dans [out/profil_apres_moyenne.txt](out/profil_apres_moyenne.txt).
|
|
|
|
|
La colonne « Avant » correspond à la version déjà optimisée pour
|
|
|
|
|
`chercher_mot`, dont le profil reste dans `out/profil_apres.txt`.
|
|
|
|
|
|
|
|
|
|
| Mesure | Avant | Après |
|
|
|
|
|
| --- | ---: | ---: |
|
|
|
|
|
| Appels à `longueur_moyenne` | 5 867 | 1 |
|
|
|
|
|
| Appels à `longueur()` depuis `longueur_moyenne` | 34 415 822 | 5 866 |
|
|
|
|
|
| Appels à `longueur()` dans tout le programme | 36 009 819 | 1 599 863 |
|
|
|
|
|
| Temps réel sans profiling (`gcc -O0`) | 1,46 s | 0,88 s |
|
|
|
|
|
|
|
|
|
|
Cette mesure ponctuelle sur la même machine donne environ 40 % de temps
|
|
|
|
|
en moins, soit une accélération d'environ 1,66 par rapport à l'étape précédente.
|
|
|
|
|
|
|
|
|
|
Validation : compilation avec `-Wall -Wextra -Wpedantic` sans avertissement,
|
|
|
|
|
puis comparaison du rapport et de la sortie console avant/après, avec et
|
|
|
|
|
sans `-pg`. Ils restent identiques octet par octet, y compris la moyenne
|
|
|
|
|
(`6.712`) et l'écart-type (`1.873`). L'empreinte reste **`1d53de903efd2b7c`**.
|
|
|
|
|
|
|
|
|
|
Commandes pour reproduire ce profil :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
mkdir -p out/apres_moyenne
|
|
|
|
|
gcc -O0 -pg analyseur.c -o out/apres_moyenne/analyseur
|
|
|
|
|
(cd out/apres_moyenne && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_moyenne.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 0m1.251s
|
|
|
|
|
user 0m1.214s
|
|
|
|
|
sys 0m0.004s
|
2026-10-07 11:51:31 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Remplacement de la dichotomie par une table de hachage
|
|
|
|
|
|
|
|
|
|
Un index de 40 001 cases associe chaque mot à son entrée dans le tableau.
|
|
|
|
|
`hacher_mot` calcule un entier à partir des caractères du mot ; le modulo
|
|
|
|
|
donne la première case à examiner. Chaque case contient l'indice de
|
|
|
|
|
l'entrée + 1, ou zéro si elle est libre.
|
|
|
|
|
|
|
|
|
|
En cas de collision, `chercher_mot` vérifie le mot avec `egales()` puis
|
|
|
|
|
avance à la case suivante, en revenant au début si nécessaire. Deux mots
|
|
|
|
|
ayant le même hachage restent donc bien distincts. Les nouveaux mots sont
|
|
|
|
|
ajoutés à la fin du tableau, sans décaler les entrées existantes.
|
|
|
|
|
|
|
|
|
|
La recherche et l'insertion sont en O(1) en moyenne pour ces mots de
|
|
|
|
|
longueur bornée, avec un pire cas en O(n) en cas de nombreuses collisions.
|
|
|
|
|
La limite de 20 000 mots distincts est vérifiée avant chaque nouvelle
|
|
|
|
|
insertion ; avec 40 001 cases, l'index reste rempli à moins de 50 %.
|
|
|
|
|
|
|
|
|
|
Ce choix ajoute `40 001 * sizeof(int)` octets, soit environ 156 Kio sur
|
|
|
|
|
cette machine. L'index est libéré après le comptage, avant le tri final
|
|
|
|
|
qui déplace les entrées. L'empreinte du rapport est conservée, mais cette
|
|
|
|
|
version utilise davantage de mémoire pendant le comptage.
|
|
|
|
|
|
|
|
|
|
Profil enregistré dans [out/profil_apres_hachage.txt](out/profil_apres_hachage.txt).
|
|
|
|
|
La comparaison porte sur la version précédente avec dichotomie et moyenne
|
|
|
|
|
calculée une seule fois, conservée dans `out/profil_apres_moyenne.txt`.
|
|
|
|
|
|
|
|
|
|
| Mesure | Dichotomie | Hachage |
|
|
|
|
|
| --- | ---: | ---: |
|
|
|
|
|
| Appels de `chercher_mot` | 150 182 | 150 182 |
|
|
|
|
|
| Comparaisons de chaînes dans `chercher_mot` | 1 718 447 (`comparer`) | 153 101 (`egales`) |
|
|
|
|
|
| Temps réel médian sans profiling (`gcc -O0`, 5 exécutions par version) | 0,888 s | 0,831 s |
|
|
|
|
|
|
|
|
|
|
Les recherches font environ 11,2 fois moins de comparaisons. Le temps
|
|
|
|
|
total baisse d'environ 6,4 % : le gain reste limité car la normalisation
|
|
|
|
|
et le tri occupent désormais l'essentiel du temps. L'ordre d'arrivée des
|
|
|
|
|
mots remplace l'ordre alphabétique pendant le comptage ; le tri final
|
|
|
|
|
effectue donc un nombre différent de comparaisons, tout en produisant
|
|
|
|
|
le même classement.
|
|
|
|
|
|
|
|
|
|
Les cinq mesures, réalisées en alternant les deux versions sur la même
|
|
|
|
|
machine, sont respectivement : 0,883 / 0,892 / 0,888 / 0,883 / 0,888 s
|
|
|
|
|
avant, et 0,831 / 0,830 / 0,831 / 0,832 / 0,829 s après.
|
|
|
|
|
|
|
|
|
|
Validation : compilation sans avertissement avec `-Wall -Wextra -Wpedantic`,
|
|
|
|
|
rapport et sortie console identiques octet par octet sur les dix
|
|
|
|
|
exécutions et avec `-pg`. L'empreinte reste **`1d53de903efd2b7c`**.
|
|
|
|
|
Des tests temporaires sous AddressSanitizer et UndefinedBehaviorSanitizer
|
|
|
|
|
ont vérifié les collisions avec retour à la première case, les doublons,
|
|
|
|
|
les mots de 0/1/31 caractères, 10 000 comptages comparés à une référence
|
|
|
|
|
linéaire, les 20 000 entrées possibles et le refus d'un dépassement.
|
|
|
|
|
Le programme complet a également été exécuté avec ces deux outils,
|
|
|
|
|
sans erreur et avec une sortie identique.
|
|
|
|
|
|
|
|
|
|
Commandes pour reproduire ce profil :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
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)
|
|
|
|
|
```
|