forked from menault/2026_TD3_DEV51_Qualite_Algo
Optimiser F10 en conservant la fin du rapport
This commit is contained in:
+136
-2
@@ -163,7 +163,7 @@ Le tableau reprend l'analyse initiale de `notes.md`, avec la complexité
|
||||
cyclomatique de chaque fonction. Les complexités temporelles sont données
|
||||
au pire cas, en faisant varier les tailles des données, même si leurs valeurs
|
||||
sont fixées par des constantes dans le programme. Les objectifs sont ceux
|
||||
du tableau de la page 67 du TD ; ils restent à atteindre.
|
||||
du tableau de la page 67 du TD ; la section 4 présente leur réalisation.
|
||||
|
||||
| ID | Fonction | Calcule | Actuelle | M | Objectif |
|
||||
|:---:|---|---|---|:---:|---|
|
||||
@@ -261,7 +261,7 @@ Un tableau de `C` indicateurs, initialisé à zéro, remplace la recherche
|
||||
dans les transactions précédentes. Une transaction marque son client à son
|
||||
premier passage et incrémente le compteur. Le coût devient **`O(n + C)`**
|
||||
au lieu de `O(n²)`, avec `O(C)` de mémoire. Les identifiants de clients sont
|
||||
densement compris entre 0 et `C - 1`, comme le garantit le générateur.
|
||||
compris entre 0 et `C - 1`, comme le garantit le générateur.
|
||||
La complexité cyclomatique passe de **5 à 3**. Les tests couvrent l'absence
|
||||
de transactions, les doublons, les identifiants extrêmes et tous les clients.
|
||||
|
||||
@@ -364,3 +364,137 @@ dépassant l'objectif. Les tests confrontent les 20 montants situés aux
|
||||
seuils et à leurs voisins, y compris `LONG_MIN` et `LONG_MAX`, à la fonction
|
||||
initiale, pour 20 clients et 210 jours (cycle commun des modulos 7 et 30),
|
||||
soit **84 000 classifications**.
|
||||
|
||||
### F10 — Conserver la fin du rapport
|
||||
|
||||
`ajouter_ligne` reçoit le pointeur sur le caractère nul final, copie la ligne
|
||||
et renvoie le nouveau pointeur de fin. `main` le conserve entre les appels.
|
||||
La fonction `longueur` disparaît : les caractères déjà écrits ne sont plus
|
||||
relus. Un ajout coûte **`O(l)`** pour une ligne de longueur `l`, et la
|
||||
construction complète **`O(L)`** au lieu de `O(L²)` pour des lignes de taille
|
||||
bornée. La mémoire auxiliaire reste **`O(1)`** et F10 garde **M = 2**.
|
||||
|
||||
Les tests vérifient la concaténation et le pointeur renvoyé après 200 ajouts,
|
||||
les lignes vides et une ligne remplissant exactement le tampon disponible.
|
||||
Le rapport complet garde ses **6 017 lignes et 120 348 octets**.
|
||||
|
||||
### Bilan des complexités
|
||||
|
||||
| Fonction | Avant | Après | M avant → après |
|
||||
|---|---|---|:---:|
|
||||
| F1 | `O(n²)` | `O(n log n)` au pire cas | 6 → 3 |
|
||||
| F2 | `O(n²)` | `O(n + C)` | 5 → 3 |
|
||||
| F3 | `O(n × C)` | `O(n + C)` | 4 → 3 |
|
||||
| F4 | `O(n × J)` | `O(n + J)` | 4 → 3 |
|
||||
| F5 | `O(J³)` | `O(J)` | 5 → 4 |
|
||||
| F6 | `O(n)` par requête | `O(1)` par requête après indexation `O(n)` | 3 → 3 |
|
||||
| F7 | `O(n²)` | `O(n log n)` | 4 → 3 |
|
||||
| F8 | `O(2^(S + k))`, borne générale | `O(S × k)` pour `k ≥ 1` | 4 → 5 |
|
||||
| F9 | `O(1)` | `O(1)` | 14 → 3 |
|
||||
| F10 | `O(L²)` au total, lignes de taille bornée | `O(L)` au total | 2 → 2 |
|
||||
|
||||
M des auxiliaires ajoutés : `comparer_transactions` **2**,
|
||||
`trier_intervalle` **6**, `indexer_par_id` **2**,
|
||||
`compter_paires_triees` **9**, `categorie_debit` **5**,
|
||||
`categorie_credit` **7**. Avec **M = 9** pour `main`, le maximum hors
|
||||
génération est désormais **9**, contre **14** initialement.
|
||||
|
||||
### Validation à chaque commit et mesure finale
|
||||
|
||||
Le journal [validations_optimisations.csv](build/validations_optimisations.csv)
|
||||
conserve les dix vérifications successives : fonction optimisée, empreinte,
|
||||
égalité du rapport, temps d'une exécution et SHA-256 du fichier `banque.c`
|
||||
testé. Chaque ligne correspond au code du commit de cette optimisation.
|
||||
Les dix empreintes sont **`ff68d260a7a58441`**, et les dix rapports sont
|
||||
identiques octet par octet à celui de la version initiale.
|
||||
|
||||
La validation finale comprend :
|
||||
|
||||
- `make test` : tests de cas limites et comparaisons aux versions de référence ;
|
||||
- `make check` : compilation en `-O2` et vérification de l'empreinte ;
|
||||
- `make profile` : compilation en `-O0 -g -pg`, même sortie et même rapport ;
|
||||
- les tests et une exécution complète compilés avec
|
||||
`-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer` : aucune erreur
|
||||
mémoire ou de comportement indéfini signalée ;
|
||||
- comparaison du code : constantes, génération des données et chaînes de
|
||||
formatage du rapport inchangées.
|
||||
|
||||
La commande pour relancer les tests avec les vérifications mémoire est :
|
||||
|
||||
```bash
|
||||
make test CFLAGS='-std=c11 -Wall -Wextra -Wpedantic -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer'
|
||||
```
|
||||
|
||||
Une nouvelle exécution de la version initiale prend **2,437683 s**. Pour
|
||||
la version finale compilée avec les mêmes options en `-O2`, les cinq temps
|
||||
réels sont **0,010910 ; 0,011111 ; 0,010734 ; 0,011242 ; 0,010525 s**,
|
||||
soit une médiane de **0,010910 s**, environ **11 ms**. Les mesures utilisent
|
||||
`time.perf_counter()` autour du processus entier et incluent l'écriture du
|
||||
rapport ; elles sont conservées dans [temps_apres.txt](build/temps_apres.txt).
|
||||
La référence étant une seule exécution et les temps dépendant de la machine,
|
||||
cette comparaison donne un ordre de grandeur du gain, pas un classement.
|
||||
|
||||
### Profil gprof après optimisation
|
||||
|
||||
Le profil d'une exécution finale est conservé dans
|
||||
[profil_apres.txt](build/profil/profil_apres.txt), ainsi que dans le fichier
|
||||
courant [profil.txt](build/profil/profil.txt). Il a été obtenu avec :
|
||||
|
||||
```bash
|
||||
make profile
|
||||
cp build/profil/profil.txt build/profil/profil_apres.txt
|
||||
```
|
||||
|
||||
Cette exécution ne fournit qu'environ **0,01 s échantillonnée**, attribuée
|
||||
au tri fusion. Un seul échantillon ne permet pas d'interpréter les parts de
|
||||
temps. Pour mieux distinguer les coûts restants, le programme complet est
|
||||
donc répété **200 fois dans le même processus instrumenté**, avec remise à
|
||||
la graine initiale entre les passages. La sortie standard est redirigée,
|
||||
le rapport est toujours écrit, et le dernier rapport est comparé à la
|
||||
référence. Cette répétition concerne uniquement le pilote de mesure ;
|
||||
`banque.c` et ses constantes ne sont pas modifiés.
|
||||
|
||||
Le profil cumulé est enregistré dans
|
||||
[profil_apres_repete.txt](build/profil/profil_apres_repete.txt). Il représente
|
||||
environ **2,07 s de temps CPU échantillonné pour 200 traitements** : les
|
||||
nombres d'appels et les temps du tableau suivant sont ceux du lot entier.
|
||||
|
||||
| Fonction | Appels hors récursion | Temps propre cumulé (s) | Part du temps |
|
||||
|---|---:|---:|---:|
|
||||
| `trier_intervalle` | 400 | 1,19 | 57,25 % |
|
||||
| `comparer_transactions` | 163 486 000 | 0,29 | 14,01 % |
|
||||
| `generer_transactions` | 200 | 0,14 | 6,76 % |
|
||||
| `compter_paires_triees` | 200 | 0,08 | 3,86 % |
|
||||
| `ajouter_ligne` | 1 203 400 | 0,04 | 1,93 % |
|
||||
| `rechercher_par_id` | 6 000 000 | 0,01 | 0,48 % |
|
||||
| `meilleure_periode` | 200 | 0,00 | 0,00 % |
|
||||
| `nb_facons_rendre` | 200 | 0,00 | 0,00 % |
|
||||
|
||||
Les deux tris de chaque traitement, celui de F7 sur une copie et celui du
|
||||
rapport, expliquent les 400 appels initiaux de `trier_intervalle`. Le tri
|
||||
fusion et ses comparaisons représentent désormais l'essentiel du coût.
|
||||
Les centaines de millions d'appels récursifs de F8 ont disparu, les
|
||||
recherches F6 coûtent très peu et F10 ne rescane plus les préfixes du
|
||||
rapport. Les valeurs à 0,00 s restent des arrondis de l'échantillonnage.
|
||||
Les durées brutes de ce lot ne se comparent pas à celles du profil initial
|
||||
sur une seule exécution, ni aux temps réels mesurés en `-O2`.
|
||||
|
||||
Pour reproduire ce profil cumulé depuis la racine :
|
||||
|
||||
```bash
|
||||
tmp_profil=$(mktemp -d)
|
||||
gcc -std=c11 -Wall -Wextra -Wpedantic -O0 -g -pg -x c -o "$tmp_profil/banque" - <<EOF
|
||||
#define main banque_main
|
||||
#include "$(pwd)/banque.c"
|
||||
#undef main
|
||||
int main(void) {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
etat_alea = GRAINE;
|
||||
if (banque_main() != 0) return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
EOF
|
||||
(cd "$tmp_profil" && ./banque > /dev/null && gprof -b ./banque gmon.out) > build/profil/profil_apres_repete.txt
|
||||
rm -rf "$tmp_profil"
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user