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)
|
|
|
|
|
```
|
2026-10-07 12:12:56 +02:00
|
|
|
|
|
|
|
|
## 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
|
2026-10-07 12:20:50 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Remplacement du tri à bulles par un tri par tas
|
|
|
|
|
|
|
|
|
|
Le tri à bulles effectuait systématiquement n(n - 1)/2 comparaisons,
|
|
|
|
|
soit 17 202 045 appels à `doit_echanger` pour 5 866 mots distincts.
|
|
|
|
|
|
|
|
|
|
`trier_entrees` utilise maintenant un tri par tas. La racine du tas contient
|
|
|
|
|
l'entrée qui doit apparaître le plus tard dans le classement. Elle est
|
|
|
|
|
échangée avec la dernière entrée de la partie non triée, puis `retablir_tas`
|
|
|
|
|
restaure cette propriété sur les entrées restantes. La construction du tas
|
|
|
|
|
coûte O(n), puis les extractions coûtent O(log n) chacune : le tri complet
|
|
|
|
|
passe de O(n²) à O(n log n), y compris dans le pire cas.
|
|
|
|
|
|
|
|
|
|
Le tri travaille directement dans le tableau, avec O(1) de mémoire
|
|
|
|
|
supplémentaire. `doit_echanger` reste inchangée : fréquence décroissante,
|
|
|
|
|
puis ordre alphabétique croissant. Chaque échange déplace l'entrée entière,
|
|
|
|
|
y compris la longueur mémorisée.
|
|
|
|
|
|
|
|
|
|
Profil enregistré dans [out/profil_apres_tri.txt](out/profil_apres_tri.txt).
|
|
|
|
|
La comparaison porte sur la version précédente, décrite dans
|
|
|
|
|
`out/profil_apres_longueurs.txt`.
|
|
|
|
|
|
|
|
|
|
| Mesure | Tri à bulles | Tri par tas |
|
|
|
|
|
| --- | ---: | ---: |
|
|
|
|
|
| Appels à `doit_echanger` pendant le tri | 17 202 045 | 129 602 |
|
|
|
|
|
| Appels à `comparer` pour départager les fréquences égales | 12 172 087 | 32 813 |
|
|
|
|
|
| Temps réel médian du programme sans profiling (`gcc -O0`, 5 exécutions par version) | 0,367 s | 0,113 s |
|
|
|
|
|
|
|
|
|
|
Le tri effectue environ 133 fois moins de comparaisons d'entrées.
|
|
|
|
|
Le temps total baisse de 69,3 %, soit une accélération d'environ 3,25.
|
|
|
|
|
Les cinq mesures, réalisées en alternant les deux versions sur la même
|
|
|
|
|
machine, sont respectivement : 0,365 / 0,363 / 0,372 / 0,383 / 0,367 s
|
|
|
|
|
avant, et 0,112 / 0,113 / 0,115 / 0,113 / 0,112 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`**.
|
|
|
|
|
Des tests temporaires ont comparé le résultat à `qsort` sur des tableaux
|
|
|
|
|
de 0 à 20 000 entrées, aléatoires, déjà triés ou inversés. Ils vérifient
|
|
|
|
|
aussi les fréquences égales, les doublons, les préfixes, les valeurs
|
|
|
|
|
extrêmes de fréquence et la conservation des longueurs des mots.
|
|
|
|
|
|
|
|
|
|
Commandes pour reproduire ce profil :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
mkdir -p out/apres_tri
|
|
|
|
|
gcc -O0 -pg analyseur.c -o out/apres_tri/analyseur
|
|
|
|
|
(cd out/apres_tri && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_tri.txt)
|
|
|
|
|
```
|
2026-10-07 12:21:17 +02:00
|
|
|
|
|
|
|
|
```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.354s
|
|
|
|
|
user 0m0.314s
|
|
|
|
|
sys 0m0.008s
|
2026-10-07 13:49:42 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Réduction des vérifications dans `analyser_bigrammes`
|
|
|
|
|
|
|
|
|
|
L'ancienne boucle examinait des fenêtres de trois caractères et appelait
|
|
|
|
|
`est_lettre` jusqu'à trois fois par fenêtre. Les caractères des fenêtres
|
|
|
|
|
voisines étaient donc vérifiés plusieurs fois, pour un total de
|
|
|
|
|
3 757 672 appels dans cette fonction.
|
|
|
|
|
|
|
|
|
|
Les lignes sont déjà normalisées avant l'analyse : elles ne contiennent
|
|
|
|
|
que des lettres de `a` à `z` et des espaces. La nouvelle boucle utilise
|
|
|
|
|
cette propriété, explicitée dans le commentaire de la fonction, et
|
|
|
|
|
conserve les indices des deux lettres précédentes. Chaque nouvelle lettre
|
|
|
|
|
complète un trigramme si deux lettres la précèdent. Un espace ou une
|
|
|
|
|
nouvelle ligne réinitialise cet historique.
|
|
|
|
|
|
|
|
|
|
Les trigrammes qui se chevauchent sont conservés, sans compter à travers
|
|
|
|
|
les séparateurs. Le calcul du meilleur bigramme et les règles de départage
|
|
|
|
|
restent identiques. Les deux versions sont en O(nombre de caractères) :
|
|
|
|
|
le gain vient des vérifications et des appels évités, sans tableau
|
|
|
|
|
supplémentaire.
|
|
|
|
|
|
|
|
|
|
Profil enregistré dans [out/profil_apres_bigrammes.txt](out/profil_apres_bigrammes.txt).
|
|
|
|
|
La comparaison porte sur la version précédente, décrite dans
|
|
|
|
|
`out/profil_apres_tri.txt`.
|
|
|
|
|
|
|
|
|
|
| Mesure | Avant | Après |
|
|
|
|
|
| --- | ---: | ---: |
|
|
|
|
|
| Appels à `est_lettre` depuis `analyser_bigrammes` | 3 757 672 | 0 |
|
|
|
|
|
| Appels à `est_lettre` dans tout le programme | 5 297 256 | 1 539 584 |
|
|
|
|
|
| Temps réel médian sans profiling (`gcc -O0`, 10 exécutions par version) | 0,1252 s | 0,1123 s |
|
|
|
|
|
|
|
|
|
|
Le gain médian mesuré est de 10,3 %. Les exécutions sont courtes et les
|
|
|
|
|
temps fluctuent ; cette valeur est indicative. Les mesures ont été faites
|
|
|
|
|
en alternant les deux versions sur la même machine :
|
|
|
|
|
|
|
|
|
|
- Avant : 0,1339 / 0,1132 / 0,1220 / 0,1146 / 0,1403 / 0,1118 / 0,1268 /
|
|
|
|
|
0,1235 / 0,1430 / 0,1373 s.
|
|
|
|
|
- Après : 0,1332 / 0,1035 / 0,1077 / 0,1133 / 0,1178 / 0,1131 / 0,1095 /
|
|
|
|
|
0,0994 / 0,1114 / 0,1193 s.
|
|
|
|
|
|
|
|
|
|
Validation : compilation sans avertissement avec `-Wall -Wextra -Wpedantic`,
|
|
|
|
|
rapport et sortie console identiques octet par octet sur les vingt
|
|
|
|
|
exécutions, avec `-pg` et sous AddressSanitizer et UndefinedBehaviorSanitizer.
|
|
|
|
|
L'empreinte reste **`1d53de903efd2b7c`**. Le bigramme reste `me`, avec
|
|
|
|
|
17 607 occurrences et `n` comme troisième lettre la plus fréquente.
|
|
|
|
|
Un test temporaire a comparé 43 cas à l'ancienne fonction : entrées vides
|
|
|
|
|
ou courtes, chevauchements, espaces, changements de ligne, égalités,
|
|
|
|
|
normalisation et données pseudo-aléatoires.
|
|
|
|
|
|
|
|
|
|
Autres pistes, à mesurer séparément :
|
|
|
|
|
|
|
|
|
|
- `est_mot_vide` provoque encore 12 045 671 appels à `egales`. Réutiliser
|
|
|
|
|
la longueur `k` pour exclure les mots plus longs que tous les mots vides,
|
|
|
|
|
puis consulter un index de hachage séparé, reste la priorité. L'ordre
|
|
|
|
|
du tableau `MOTS_VIDES`, utilisé pour générer le texte, doit être conservé.
|
|
|
|
|
- `normaliser_ligne` pourrait utiliser une table de conversion des octets
|
|
|
|
|
en minuscules ou espaces, pour regrouper classification et conversion.
|
|
|
|
|
- `decouper_lignes` pourrait utiliser des pointeurs dans le texte et
|
|
|
|
|
remplacer les sauts de ligne par `\0`, évitant les allocations et copies
|
|
|
|
|
par ligne. La libération de la mémoire devrait être adaptée en conséquence.
|
|
|
|
|
|
|
|
|
|
Commandes pour reproduire ce profil :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
mkdir -p out/apres_bigrammes
|
|
|
|
|
gcc -O0 -pg analyseur.c -o out/apres_bigrammes/analyseur
|
|
|
|
|
(cd out/apres_bigrammes && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_bigrammes.txt)
|
|
|
|
|
```
|
2026-10-07 13:59:32 +02:00
|
|
|
|
|
|
|
|
## Optimisation de `est_mot_vide`
|
|
|
|
|
|
|
|
|
|
La recherche linéaire dans les 60 entrées de `MOTS_VIDES` est remplacée
|
|
|
|
|
par un index de hachage de 121 cases, construit au premier appel seulement.
|
|
|
|
|
Chaque case contient l'indice d'un mot + 1, ou zéro si elle est libre.
|
|
|
|
|
Les collisions sont résolues en avançant à la case suivante, avec retour
|
|
|
|
|
au début. Le doublon `ou` réutilise la même case : l'index contient donc
|
|
|
|
|
59 mots distincts et reste rempli à moins de 50 %.
|
|
|
|
|
|
|
|
|
|
L'initialisation calcule aussi la longueur maximale des mots vides,
|
|
|
|
|
actuellement 5 caractères. `est_mot_vide` reçoit la longueur `k`, déjà
|
|
|
|
|
connue lors du découpage, et rejette immédiatement les mots plus longs.
|
|
|
|
|
Sur ce texte, 107 019 mots sont ainsi exclus avant même le hachage.
|
|
|
|
|
Les autres recherches sont en O(1) en moyenne pour ces mots de longueur
|
|
|
|
|
bornée, avec un pire cas linéaire en cas de nombreuses collisions.
|
|
|
|
|
|
|
|
|
|
L'index est séparé de `MOTS_VIDES` : la liste originale, son ordre et son
|
|
|
|
|
doublon sont conservés pour produire exactement le même texte.
|
|
|
|
|
L'index et la longueur maximale occupent 488 octets sur cette machine,
|
|
|
|
|
sans allocation dynamique supplémentaire.
|
|
|
|
|
|
|
|
|
|
Profil enregistré dans [out/profil_apres_mots_vides.txt](out/profil_apres_mots_vides.txt).
|
|
|
|
|
La comparaison porte sur la version précédente, décrite dans
|
|
|
|
|
`out/profil_apres_bigrammes.txt`.
|
|
|
|
|
|
|
|
|
|
| Mesure | Avant | Après |
|
|
|
|
|
| --- | ---: | ---: |
|
|
|
|
|
| Appels à `est_mot_vide` | 250 000 | 250 000 |
|
|
|
|
|
| Appels à `egales` depuis `est_mot_vide` | 12 045 671 | 202 444 |
|
|
|
|
|
| Appels à `egales` lors de l'initialisation de l'index | 0 | 28 |
|
|
|
|
|
| Appels à `egales` dans tout le programme | 12 198 772 | 355 573 |
|
|
|
|
|
| Temps réel médian sans profiling (`gcc -O0`, 10 exécutions par version) | 0,1007 s | 0,0543 s |
|
|
|
|
|
|
|
|
|
|
La recherche des mots vides effectue environ 60 fois moins de comparaisons.
|
|
|
|
|
Le temps total baisse de 46,1 %, soit une accélération d'environ 1,86.
|
|
|
|
|
Les mesures ont été réalisées en alternant les deux versions sur la même
|
|
|
|
|
machine :
|
|
|
|
|
|
|
|
|
|
- Avant : 0,0998 / 0,0953 / 0,0997 / 0,1015 / 0,1170 / 0,1067 / 0,1102 /
|
|
|
|
|
0,0999 / 0,1018 / 0,0989 s.
|
|
|
|
|
- Après : 0,0531 / 0,0535 / 0,0565 / 0,0542 / 0,0554 / 0,0544 / 0,0577 /
|
|
|
|
|
0,0579 / 0,0520 / 0,0522 s.
|
|
|
|
|
|
|
|
|
|
Validation : compilation sans avertissement avec `-Wall -Wextra -Wpedantic`,
|
|
|
|
|
rapport et sortie console identiques octet par octet sur les vingt
|
|
|
|
|
exécutions, avec `-pg` et sous AddressSanitizer et UndefinedBehaviorSanitizer.
|
|
|
|
|
L'empreinte reste **`1d53de903efd2b7c`**.
|
|
|
|
|
Un test temporaire a comparé les réponses à une recherche linéaire pour
|
|
|
|
|
les 60 entrées, leurs préfixes et suffixes, les 18 278 chaînes minuscules
|
|
|
|
|
de 1 à 3 caractères, 50 000 mots pseudo-aléatoires et les 7 000 mots du
|
|
|
|
|
vocabulaire. Un dictionnaire synthétique dans un processus séparé a
|
|
|
|
|
également vérifié les collisions avec retour à la première case et
|
|
|
|
|
l'adaptation automatique à des mots vides de plus de 5 caractères.
|
|
|
|
|
|
|
|
|
|
Commandes pour reproduire ce profil :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
mkdir -p out/apres_mots_vides
|
|
|
|
|
gcc -O0 -pg analyseur.c -o out/apres_mots_vides/analyseur
|
|
|
|
|
(cd out/apres_mots_vides && ./analyseur && gprof -b ./analyseur gmon.out > ../profil_apres_mots_vides.txt)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
[rabot@salle222-12 out]$ gcc -O0 ../analyseur.c -o analyseur
|
|
|
|
|
[rabot@salle222-12 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.078s
|
|
|
|
|
user 0m0.059s
|
|
|
|
|
sys 0m0.001s
|
|
|
|
|
```
|