From f2eb2c2f1ee35b435f080ced06f12c522fc2b4d5 Mon Sep 17 00:00:00 2001 From: rabot Date: Wed, 21 Oct 2026 11:23:07 +0200 Subject: [PATCH] Optimiser F10 en conservant la fin du rapport --- COMPTE_RENDU.md | 138 ++++++++++++++++++- banque.c | 41 +++--- build/banque | Bin 21160 -> 21616 bytes build/profil/banque | Bin 28888 -> 30752 bytes build/profil/gmon.out | Bin 5606 -> 6372 bytes build/profil/profil.txt | 191 +++++++++++++++------------ build/profil/profil_apres.txt | 126 ++++++++++++++++++ build/profil/profil_apres_repete.txt | 135 +++++++++++++++++++ build/temps_apres.txt | 9 ++ build/validations_optimisations.csv | 11 ++ notes.md | 32 +++-- tests/test_banque.c | 30 +++++ 12 files changed, 590 insertions(+), 123 deletions(-) create mode 100644 build/profil/profil_apres.txt create mode 100644 build/profil/profil_apres_repete.txt create mode 100644 build/temps_apres.txt create mode 100644 build/validations_optimisations.csv diff --git a/COMPTE_RENDU.md b/COMPTE_RENDU.md index 5f1e458..12b096e 100644 --- a/COMPTE_RENDU.md +++ b/COMPTE_RENDU.md @@ -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" - < /dev/null && gprof -b ./banque gmon.out) > build/profil/profil_apres_repete.txt +rm -rf "$tmp_profil" +``` diff --git a/banque.c b/banque.c index face485..802edfd 100644 --- a/banque.c +++ b/banque.c @@ -300,23 +300,13 @@ int categorie(const Transaction *t) } /* F10 : ajoute une ligne à la fin du rapport */ -int longueur(const char *s) +/* fin_rapport désigne le '\0' courant ; l'appelant conserve la nouvelle fin. */ +char *ajouter_ligne(char *fin_rapport, const char *ligne) { - int n = 0; - while (s[n] != '\0') - n++; - return n; -} - -void ajouter_ligne(char *rapport, const char *ligne) -{ - int fin = longueur(rapport); - int i = 0; - while (ligne[i] != '\0') { - rapport[fin + i] = ligne[i]; - i++; - } - rapport[fin + i] = '\0'; + while (*ligne != '\0') + *fin_rapport++ = *ligne++; + *fin_rapport = '\0'; + return fin_rapport; } /* ------------------------------------------------------------------ */ @@ -339,6 +329,7 @@ int main(void) char *rapport = malloc(1 << 20); char ligne[128]; rapport[0] = '\0'; + char *fin_rapport = rapport; /* recherches par id, dans l'ordre d'origine */ static const Transaction *index_ids[NB_TRANSACTIONS]; @@ -374,30 +365,30 @@ int main(void) /* rapport */ snprintf(ligne, sizeof(ligne), "clients distincts;%d\n", nb_clients); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); snprintf(ligne, sizeof(ligne), "somme des requetes;%ld\n", somme_requetes); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); snprintf(ligne, sizeof(ligne), "meilleure periode;%d;%d;%ld\n", debut, fin, meilleure); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); snprintf(ligne, sizeof(ligne), "paires opposees;%ld\n", nb_paires); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); snprintf(ligne, sizeof(ligne), "facons de rendre %d;%ld\n", MONTANT_MONNAIE, nb_facons); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); for (int c = 0; c < 12; c++) { snprintf(ligne, sizeof(ligne), "categorie;%d;%d\n", c, categories[c]); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); } for (int c = 0; c < NB_CLIENTS; c++) { snprintf(ligne, sizeof(ligne), "client;%d;%ld\n", c, totaux_clients[c]); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); } for (int j = 0; j < NB_JOURS; j++) { snprintf(ligne, sizeof(ligne), "jour;%d;%ld\n", j, totaux_jours[j]); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); } for (int i = 0; i < NB_TRANSACTIONS; i += 10) { snprintf(ligne, sizeof(ligne), "rang;%d;%d;%ld\n", i, t[i].id, t[i].montant); - ajouter_ligne(rapport, ligne); + fin_rapport = ajouter_ligne(fin_rapport, ligne); } FILE *f = fopen("rapport.txt", "w"); diff --git a/build/banque b/build/banque index 0ae2b468574f5d6d707dd61f119e9b6a5fcfc01f..aaf9c8616c309e790bc1ba723b3716615061d657 100755 GIT binary patch delta 7982 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 delta 11404 zcmZ4Rf$_#e#t9Na2Lu?vzyVA$FhrzJ)LO>1g9F0d!7=fcKBpLj(p43Bv72V3^#@*edkG48ljylcku{gfz?{!U%eD5R)1<)m=<#ibo6~sxfGY zFvF3_7n#I`5=l|(>xLH)-73w4$qd~us}?d`DgdqQ&RmNlh-gSFbYgQ%Ph_T z^6*JZ2l>g0%rcB>lLcAzSr0HUFepq`WEN&Dm>kIBKKTKQ1f#`dMHX>JZ7}HpCY``! zEr?{j1Jl8p$iTp$1kyYC0;|O22v&(nY@(B8*rIvTl5_HlQyJ01G(tPE42Vj`KDybKI14D+G0|OJo7LaBJ28JEJ5C$~%7z8FCWEYn_AP5ol2!PNqS&zvd*_|iXaPaUR z@Q28-F)%Q2O!nmvpX|fI!+QZqlxK1+hqxp!L>ZWTfF#Q|c`ZjjBiCeGPH{Ooq33=IE858E*?d`Z*emu~^< z{>(4;tD7~=o`IqDKnc^~385anwm;te|L@UjS|`fDaGcfMo`Hdpfq}uJ^XY%lWp)e< z9^JOn?HCwhUn+`Dt`$_XV6%`fu`lAp^$0n=c5>Wm0hP=&U`_S$gES>kW{- zFV8YEFdTOUB_xKIs~H&>CT|dzoNOS+#eTJy zRMX0hFo%V^o1@zm)hrgSZU)PP{C$U@Mya0g=;dMZ=w|fj4&?CYbba8_8T!Jb+fl%y z)Aa+2X4VrPy(UZ`Co-OxoGUKHi6UDMHOKXVM|bE8k4{F9&Oi>2Zr2YUosI&{u6G!_ zU5_-sVAOWKVd;8@zsLFC|NqUdH=v@Ht~dC5RQ~<{KVibj!!Npi{{J89(fQP)`Hg}{ zXYGs5(kI7VKY#-4xa$`X^}77H>kkm)xa%Jf^*XTIb@ByC8CDOLZr3l9FG|XqTOKUs zJns4flr~=TG`s#`>~{TQ?fRu=d2{U#hEkSh*AI-%u3s3tUB6hnKB;N!cKvYj@QX)3 zCf7*Hu)bUm3Y!U1(ws2!t0$k6RA9Aq{ZSe;d4iNED=1k1Oui^C?b00}(CzvI6k8@o zK~C}LcD>=j_`!qmlt-rk+}pf;$(^AOU;*wv`JAL2CxfNygHri!S5Qz(dAt@xglEmh9Uw=a zJpAJRR}6Dt9)KpA*IC`JADRy^cDjCf9o+5u0TgeO6{J`hzkpcv-3|gCj3+!=50tWc zG`s#lP8Lf+$)wx$11O2~d33w}IC=Pm6V$$7kLEWLowaW|OJ6kCzF{b3-vLf*VIWQg zTXXFTkPyg?ubsPHUo^j91czZONW`O;wMK-2p__%NJM>MQ=Amxa7oEpCufJIM|NnoF z&cofVFM0$1ce}pnyw-XAMGr`j@rFltEXekwU*Nvw@aU}l0dkZF$Oo+l_&cA03PMn7 z1PRza*!cf{ZQ0~Eih9hfC59l{kgb#p?8=uJj0_ArKy}c|I7U#J>G}g~h;7B2|Nm>b zb}%zAFuZgDl_R?p4RyG7uz>g+AU&)g)<0x>S`U*gI!hmb)pZ_!vEu*#|DCRX zpeggU2FRCSQyzFQUg-Ay(;fPQ@svmMDNxyC?ZJ48e;Y#w6DT%O6hf>!(jEGz+w}+I z1%x|}F))A%9@bBj4U`LcCVhmuzwwR1#iR3@N9XYuwSWHq_voGq3LlSN(+zx+v(;oxXX8?B{^$Sy?p}~zx~GD@ z(!Ey#WMr>t8QJ8Vb*}9dQIQ+ zfUI!}#bwRMpCGS*6f-vN1yP{-2V~6$s5LL=GB7Z7w|ZDE(aR}F=I8v3`lYm-NjuG%KQjJdLhz~^vAmcY{j8{poHhqc^+ay7DNfChymrJ zg`jNSdB~%ebs1PSD04v+I-)8Rf^b1a7DylStN;IZFfbT`A~Ezwiifs{ss;lCLka5( zkOe0XBR9A`x<%h=GB6l^`>&d-IXO{VicxcOjdlymA}-cR1`3lK3=}4xGWa+7m|<5v zsM93Qz`zi5@Be=rMh1pA_x}G+VPs(VbMOCuP^(tw{{R1=hFi}4|NjLT85nBr|NpPT z$iNW#;Q#*`Mh1o{5B~q3!^ptk`}qI=Js|q&|NnOw85q7k{r~?DBLlvbgzz~2W1`=0cU|^_WU|^8A`~Uxh z$$O2J85<@)G!|#PIQgfsJmZJSk|y%?oe%&2X8={VZhQi5OwPP)%-T$x1Vm4(5D_~$?07c{yCI$wDkN^J* zgUorW?*1=bdQ;pnc>Ie^=6{=OK$xK15l^km4Sf) z#s^gwp#HANZLsuzP^B3SvJ=V&r!!Fcg7QHlE?l651?7Vhn=Be%6~zAlF%RTkT?Phb zhLR7P-OP*FHgS0{N(u-;TzCN5#|O1TK_Xp~FWM?Fs!jf68>iF(GMRya0o1z($$bDd z@fa8wK$e5}-xwwr+1VL^@-v7F3L_8=3PKPK%HtsV2UGw`F??W~{K(EucLCJc|NsBz zgOtHs@BzxNgUbH|@h2}V7oWt{GWmsl1Sd!_2zyK}bg<%*gnC3@WU?Z&SbaMWXdHwQ z6ow2E^2`hjppanz=|Lz2X@+194)zd61_llm_DDts21yPMMh;ab1_ll$_70FRBbz#y z$Lb6!C0JN9^GX=_qd;RF9IUe#7#R4|K;tPt85tOubfxt`?0S&7Ofw|G61$lg7?@^C zgV?OILAn^(^q3eJ_(5#xSBwk{H<*n%!kHKt7(xANW>a>M(F|a>Gh1*@1MAqzz`($4 z3FZhe9%W)+V73BtBp8=6GcYh)gE0UK=x}e9s}`g!BPf{ZXgrvz#I$4 zTOf`-nB%}W8>Gw$%<*7M2N~xK<^(X_1zF?*=0q@ZfjDkr1xySK35=l9huK4^CT89Gn#?;z8qCd3=HLrpw>ULA9o!C149KPsLR0|C>^rV3~M;NbwI2n9J3=9k>72}W5?MrH;E`OFjsMmbJM+9&~~7I`p-g|V5Dfq_v0%;8`> z!_2_Is94VhN^JrR0_6-042(+5M?mQfWD3t>1_lNVF_5b`nFBz@4FdxMH)ssfoSA_E zYCpd^GXn$97EoC!1QOjUbH-Y!EpXkPd!E zJ`fKS6>NG6Ms{KW>oW%o7{xc=j+e zF#P9|Wn*Aqp7@}S{S_#^F@nRHdE$#&_7YI4U=#*9gLz{8hg$Yz1_lNmMsTz+Pkd6# z&c(>Uz{e=4%f`UK$=m=9diD+m1_nV!K|8Q0LqEh-?7<8S48kzIM`}6KGV>UO83jNt zXP$VXmfezxfk6ajc}E?4Bm)D37>qxmj=laT69a<;Bd0&uwHxZ#FEcSP$iWoMsN+my zuwmo|1upZ%1$BH`(9qZbv7RBFm4Sh?kb#wv4`Sn)TINcq5>Ui9$%4Ya8)Qf(gCxw53$+YwuhDMMA1_p-95H``3_Q(#b69mQ-7zHf!Q3?yaH9sP=h!#8O&h{ z9@H{0u!1rKIPZbt1f2Hk8Mr|i2NWCPpqM<*4iNx_BUs=$GXpm$Oh6t7>)>W!1(goG zCm9$RKJtU~Op{|^0QsK*+{gf@tvzfE3@pVAA5}rQfoWm{QwGCla7tmCn8CbN4&pvP z2*tpZ$?ydn?o1O?7#NhdfHL$WHc_Z5Sg0`05oBjz-~xriBPmcCWttel1xlii^uhU$ zX<`5qDAhaxCqSl&IZT-hPoeA#2Hw4l3=FrxIpH3-W@KOhx1PYR^=1dV_LdaPwRsG8 zpgMCHc%?xFCs^k-X^4x#?J}^=N|4SHh8;@vpzvXun86?dnqpbY1`5C6la&CtL|VBP~YA2LhA01AvYs6#@Sk3uE$ zA#Pw`%3^4T21W$)S*RqaRSJ^l0mWU15Gde4p?no8tq<})NS-O1p;HXv#uyNvfi)LY z1A;_ED;XIW%I1RF%?u0-;Qk5Nt2|ubNG;Pq1ZbH)NCVTv5O5HbS%8!IhKkj)rE*1ZEwmUQks6@-0&)LwyW1BvP17p|Y^jh)0--fgzR`Vp0aP4OBV_ zq!W}Zvl!x#oX8-0h?#-GOHdRh4WSw59D_K|OBGUG?x<(UWAM@enaVtIOMN}FDpbb{ zXnUS1lfeh-CJ$yqsN_kQWEO)j)KVX2CFpc9s4)z(jj1HJfFVE@R9G`j1Xr6PkC_=5 z4ER7cM?jnXJ3)OzaJc30Ffg#x=P?+lgB-&&(TAyo!3bQbGfm9kYlo@_HH|@rEAoId zfHBl*5e&?}(EiIlkTfVjG8t^3vN6ojP)ShJ1|-jv#b65!$^@`G?I1iMP|?kx0*XyW zEpV+MIl0eYCBlS(nZrtv(VE+m+f$fHf`NgVn^BNa2vlscqD!&zGjJ<%cnUKxu(2|L zD0UMDWTU3_LJ(a9eow80{q|_XSFN@ImYY zDdSfIn<^j%GL;+RBnAdSMFvk{eQpK@A&?$sJ}Yi(R!eSmNl#%LNsumKVFqSC8_CH_ z0~9Bp50I%B1LZSuuqIXp1_?n%RX$;EW_f9OkhcWH85kt_8MwI_7^I+1UIz*x`6Vav)|!YUNW$_XY#Id-wCc(E#SG_h)wunKsw3QuGe{gnrN^q$%qq)q zf>nkiomG;fiB+hHRgB{Vt0+e#YrPOh6RT7ct0qVxMCGxw z3Nb_*W(ZFQD}OqxEJHmL3nOzm)KD*09%k+)Rz_PRRz41}q5P4o!W=fJ1`F7*ibt}_ zbEL5^NY9b`P8KC2+dE><2rRLVV zgVckZGXdlra8!7Kqe9LL6dS^ktg0ZrVPIF)GkHObLfG8K%IIy%%E4jFDv-{~QOYU| zQk@3kn3b|>r?K*I*l^ggx`RYHVTEeOXqCmnDkqcNw=d%hov5GX;vkEO>l?JI-7h;w0W)+#wD!71^zll{Kl9g`( z)I69xIFWj>8h{Kcf%pPq9;-+xt7s|2L?{F5431A=Q%hL|%2*krj9582gju;uS-JFC zc|oRly0Qv_=ufOXKCFzkhOGP?-mIKYK}r<$Sw&B<3anvOEMb+h0qFp%^k5Z*Xb|*c zI0?>pWR7(oT$PA*=9OR=%~Ytn*k!BU$;bu?C%FjhLJhnU%z(EzHr(qzfYK zKm?ejFU-ov(F{_}sn299%qj;mm!km^r6sIBnb#kljvd-}&0CzhqgCnXlA zPTmkB%8`?wmtLA$S~U4#jOt{eSWWhn)U3q#veL;`u>pJ~`6Y=t@db%R@yR)vsgqa7 znoj-~tHzm^6rYxuoS#=bSu4(Ya!FhkZ+u!&YHCqxaePi@ami%K_`Q>v5{xF>Ca6zV zVwRfRz``=QCqYIMwtfrRTVxOh6`Bmv3<;ADCg?K;O#YZ4ZrA`FjAVcUTTpSzz{>y| zq=X7HfSSFG4AKlKlPweV83iV1Cb}~QOx~C%?8?Z%&j25@fGR%?wF%bsZm@$$faV%N zrocMi4$z+N>&X`r#JNCYOpFYI3^AsYcO|J!UckyS`AL$9BoC-!U=Uz{Eii)^#lRpx zSuj~#(v*>bL5P8u0lm}aJlQc>oG}DU7EWHsDqas7a8_WDVsHRAY+$A^F!VxAK<{$w zXMmUt8^yGTYMBpG&&10R039U;1uIBu8AzOgmjS({y%i$v#DLziJI)B2q?TfcKy%PJ zhz17q*7ALjXAD03fbTI?7yLy`q(>;Ysntg&wm5qDxh zAMXOqXMrqsK(jawYylrbFU&@eX`o4j$s1F}4a*?LF`&1)yFubiybJ|UUEnDY1_p*j zAYHP%K_iPym`Un5Gj!FY>_d=#23`jAPW~5W=-Nm|{>dNH#Ti8>Gp36RX|q5)ix_{H zY?N_|Ex^;&*)@1`PG%D$yhNLntu@&;+iCK|?0J*zawIlq<`gkaevtQoX$IruL-~4w zu&E6g{cW;izWii`0s%qTv;&M*n(SDhF9@5gfYAYy8w>QgK0v1^KCn&RRbVCro9KYi z2N)(Z71l9MnA}*X&$R)X7B>h?-c@J@Up@rzamQ(iiG5f=UGveaA{mc}BW+2%nk!{p{tQ^ttNJ4@{uizojqwdYz4mFwo2>{MpPr3YO< zYQr?SsmzQk3)GfoU|_hxHu+GQ8K)Dd@yx)$@MCggnfSyFER$u*WjL)sy)*^}hAopF u%gseUKzRqCAqpD76QA5$uFbgtG)B$9z+gK0V7WQx(^QD3izYJ`$O8bpIM*8h diff --git a/build/profil/gmon.out b/build/profil/gmon.out index 5b4ae4ea197efc29f64796ec347d4a26f06e51d9..6c71ed4ff6cb9c102516a0801bb78bd51649e3f4 100644 GIT binary patch literal 6372 zcmYe#&Cg?GzyT&`LnNMXF)*YsFfbISCg2mwY0VTdVt zLJ-=Zl!1XU0Gr?fG(mlk$_r?M4Xg|dj0PeQvtVX!$Y)?++<+zs*6tt%QTa_2LdU&g zU|>wZCU^lVm{M_SRet>2Gh>Oz`(%RAP*5_hDw5(x{MQ`f{d~dNwDAsB?#xa z0))P0#lXOL0V+685h54{5a|@&z*}j?e^m Sa5FG4e$axbG=jzz$ngLVTS2Y> literal 5606 zcmYe#&Cg?GzyT(xK_r%NGBBhtFfbISCg5_X7#Tp#U=Zd4vp`}XH4F?a3~USx3~XT4Aj?2%Kp4b^U}mrm1_ogUP6jpx zCI)5(kO&I{E7*Pz$pAJVWD64mD3yc6n80En9*6~UA&6!G>jIepawo`DAPmyQ!oa}5 z&cMt7q9OJYPl9X#sRp?VM1$N96NB(UA|Mti7@`Lhsvx(5LW+3Z5WU2!hp2?;LXgOQ zfT*Ay35qk2>8uQl44e!+ASTI}4V+RSBm)C4gBFGfJOtyH37j6e!8$?d7~~!X1}+9r z$^nH3NQ{Aj2b>1MiogU&Jv%sUfw>Gk;M50-JCGib7>ov`H;`GNv;qogMg}${eIS*i z7*rmB!Ws1-+#pl3*VxC_p%3;t(3F(g7;C4_Pn)D!2$*xPnzSKn2?*k+d&>3Lb=71XjtQ z2vKnx>Ityz22epUQHUg1r2|wD=1s8R1gM}C)VpB83)ln=lptoIs4T!HcmSKAf-WGl>8I diff --git a/build/profil/profil.txt b/build/profil/profil.txt index 6169f6b..1eae81a 100644 --- a/build/profil/profil.txt +++ b/build/profil/profil.txt @@ -1,99 +1,126 @@ Flat profile: Each sample counts as 0.01 seconds. - % cumulative self self total - time seconds seconds calls s/call s/call name - 48.60 3.13 3.13 1 3.13 3.13 trier_par_montant - 13.20 3.98 0.85 30000 0.00 0.00 rechercher_par_id - 11.80 4.74 0.76 1 0.76 0.76 nb_paires_opposees - 10.17 5.39 0.66 1 0.66 0.66 nb_facons_rendre - 6.37 5.80 0.41 6017 0.00 0.00 longueur - 4.97 6.12 0.32 1 0.32 0.32 meilleure_periode - 1.71 6.24 0.11 1 0.11 0.11 nb_clients_distincts - 1.71 6.34 0.11 1 0.11 0.11 total_par_client - 0.78 6.39 0.05 1 0.05 0.05 total_par_jour - 0.39 6.42 0.03 30000 0.00 0.00 categorie - 0.31 6.44 0.02 _init - 0.00 6.44 0.00 149998 0.00 0.00 alea - 0.00 6.44 0.00 6017 0.00 0.00 ajouter_ligne - 0.00 6.44 0.00 1 0.00 0.00 empreinte - 0.00 6.44 0.00 1 0.00 0.00 generer_transactions - + % cumulative self self total + time seconds seconds calls ms/call ms/call name +100.00 0.01 0.01 2 5.00 5.00 trier_intervalle + 0.00 0.01 0.00 817430 0.00 0.00 comparer_transactions + 0.00 0.01 0.00 149998 0.00 0.00 alea + 0.00 0.01 0.00 30000 0.00 0.00 categorie + 0.00 0.01 0.00 30000 0.00 0.00 rechercher_par_id + 0.00 0.01 0.00 15066 0.00 0.00 categorie_debit + 0.00 0.01 0.00 14934 0.00 0.00 categorie_credit + 0.00 0.01 0.00 6017 0.00 0.00 ajouter_ligne + 0.00 0.01 0.00 2 0.00 5.00 trier_par_montant + 0.00 0.01 0.00 1 0.00 0.00 compter_paires_triees + 0.00 0.01 0.00 1 0.00 0.00 empreinte + 0.00 0.01 0.00 1 0.00 0.00 generer_transactions + 0.00 0.01 0.00 1 0.00 0.00 indexer_par_id + 0.00 0.01 0.00 1 0.00 0.00 meilleure_periode + 0.00 0.01 0.00 1 0.00 0.00 nb_clients_distincts + 0.00 0.01 0.00 1 0.00 0.00 nb_facons_rendre + 0.00 0.01 0.00 1 0.00 5.00 nb_paires_opposees + 0.00 0.01 0.00 1 0.00 0.00 total_par_client + 0.00 0.01 0.00 1 0.00 0.00 total_par_jour + + Call graph -granularity: each sample hit covers 4 byte(s) for 0.16% of 6.44 seconds +granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01 seconds index % time self children called name - -[1] 99.7 0.00 6.42 main [1] - 3.13 0.00 1/1 trier_par_montant [2] - 0.85 0.00 30000/30000 rechercher_par_id [3] - 0.76 0.00 1/1 nb_paires_opposees [4] - 0.66 0.00 1/1 nb_facons_rendre [5] - 0.00 0.41 6017/6017 ajouter_ligne [6] - 0.32 0.00 1/1 meilleure_periode [8] - 0.11 0.00 1/1 nb_clients_distincts [9] - 0.11 0.00 1/1 total_par_client [10] - 0.05 0.00 1/1 total_par_jour [11] - 0.03 0.00 30000/30000 categorie [12] - 0.00 0.00 1/1 generer_transactions [16] - 0.00 0.00 1/1 empreinte [15] + 119996 trier_intervalle [1] + 0.01 0.00 2/2 trier_par_montant [2] +[1] 100.0 0.01 0.00 2+119996 trier_intervalle [1] + 0.00 0.00 817430/817430 comparer_transactions [5] + 119996 trier_intervalle [1] ----------------------------------------------- - 3.13 0.00 1/1 main [1] -[2] 48.6 3.13 0.00 1 trier_par_montant [2] ------------------------------------------------ - 0.85 0.00 30000/30000 main [1] -[3] 13.2 0.85 0.00 30000 rechercher_par_id [3] ------------------------------------------------ - 0.76 0.00 1/1 main [1] -[4] 11.8 0.76 0.00 1 nb_paires_opposees [4] ------------------------------------------------ - 266723738 nb_facons_rendre [5] - 0.66 0.00 1/1 main [1] -[5] 10.2 0.66 0.00 1+266723738 nb_facons_rendre [5] - 266723738 nb_facons_rendre [5] ------------------------------------------------ - 0.00 0.41 6017/6017 main [1] -[6] 6.4 0.00 0.41 6017 ajouter_ligne [6] - 0.41 0.00 6017/6017 longueur [7] ------------------------------------------------ - 0.41 0.00 6017/6017 ajouter_ligne [6] -[7] 6.4 0.41 0.00 6017 longueur [7] ------------------------------------------------ - 0.32 0.00 1/1 main [1] -[8] 5.0 0.32 0.00 1 meilleure_periode [8] ------------------------------------------------ - 0.11 0.00 1/1 main [1] -[9] 1.7 0.11 0.00 1 nb_clients_distincts [9] ------------------------------------------------ - 0.11 0.00 1/1 main [1] -[10] 1.7 0.11 0.00 1 total_par_client [10] ------------------------------------------------ - 0.05 0.00 1/1 main [1] -[11] 0.8 0.05 0.00 1 total_par_jour [11] ------------------------------------------------ - 0.03 0.00 30000/30000 main [1] -[12] 0.4 0.03 0.00 30000 categorie [12] + 0.00 0.01 1/2 nb_paires_opposees [4] + 0.00 0.01 1/2 main [3] +[2] 100.0 0.00 0.01 2 trier_par_montant [2] + 0.01 0.00 2/2 trier_intervalle [1] ----------------------------------------------- -[13] 0.3 0.02 0.00 _init [13] +[3] 100.0 0.00 0.01 main [3] + 0.00 0.01 1/1 nb_paires_opposees [4] + 0.00 0.01 1/2 trier_par_montant [2] + 0.00 0.00 30000/30000 rechercher_par_id [8] + 0.00 0.00 30000/30000 categorie [7] + 0.00 0.00 6017/6017 ajouter_ligne [11] + 0.00 0.00 1/1 generer_transactions [14] + 0.00 0.00 1/1 indexer_par_id [15] + 0.00 0.00 1/1 nb_clients_distincts [17] + 0.00 0.00 1/1 total_par_client [19] + 0.00 0.00 1/1 total_par_jour [20] + 0.00 0.00 1/1 meilleure_periode [16] + 0.00 0.00 1/1 nb_facons_rendre [18] + 0.00 0.00 1/1 empreinte [13] ----------------------------------------------- - 0.00 0.00 149998/149998 generer_transactions [16] -[14] 0.0 0.00 0.00 149998 alea [14] + 0.00 0.01 1/1 main [3] +[4] 50.0 0.00 0.01 1 nb_paires_opposees [4] + 0.00 0.01 1/2 trier_par_montant [2] + 0.00 0.00 1/1 compter_paires_triees [12] ----------------------------------------------- - 0.00 0.00 1/1 main [1] -[15] 0.0 0.00 0.00 1 empreinte [15] + 0.00 0.00 817430/817430 trier_intervalle [1] +[5] 0.0 0.00 0.00 817430 comparer_transactions [5] ----------------------------------------------- - 0.00 0.00 1/1 main [1] -[16] 0.0 0.00 0.00 1 generer_transactions [16] - 0.00 0.00 149998/149998 alea [14] + 0.00 0.00 149998/149998 generer_transactions [14] +[6] 0.0 0.00 0.00 149998 alea [6] ----------------------------------------------- - + 0.00 0.00 30000/30000 main [3] +[7] 0.0 0.00 0.00 30000 categorie [7] + 0.00 0.00 15066/15066 categorie_debit [9] + 0.00 0.00 14934/14934 categorie_credit [10] +----------------------------------------------- + 0.00 0.00 30000/30000 main [3] +[8] 0.0 0.00 0.00 30000 rechercher_par_id [8] +----------------------------------------------- + 0.00 0.00 15066/15066 categorie [7] +[9] 0.0 0.00 0.00 15066 categorie_debit [9] +----------------------------------------------- + 0.00 0.00 14934/14934 categorie [7] +[10] 0.0 0.00 0.00 14934 categorie_credit [10] +----------------------------------------------- + 0.00 0.00 6017/6017 main [3] +[11] 0.0 0.00 0.00 6017 ajouter_ligne [11] +----------------------------------------------- + 0.00 0.00 1/1 nb_paires_opposees [4] +[12] 0.0 0.00 0.00 1 compter_paires_triees [12] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[13] 0.0 0.00 0.00 1 empreinte [13] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[14] 0.0 0.00 0.00 1 generer_transactions [14] + 0.00 0.00 149998/149998 alea [6] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[15] 0.0 0.00 0.00 1 indexer_par_id [15] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[16] 0.0 0.00 0.00 1 meilleure_periode [16] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[17] 0.0 0.00 0.00 1 nb_clients_distincts [17] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[18] 0.0 0.00 0.00 1 nb_facons_rendre [18] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[19] 0.0 0.00 0.00 1 total_par_client [19] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[20] 0.0 0.00 0.00 1 total_par_jour [20] +----------------------------------------------- + + Index by function name - [13] _init [16] generer_transactions [4] nb_paires_opposees - [6] ajouter_ligne [7] longueur [3] rechercher_par_id - [14] alea [8] meilleure_periode [10] total_par_client - [12] categorie [9] nb_clients_distincts [11] total_par_jour - [15] empreinte [5] nb_facons_rendre [2] trier_par_montant + [11] ajouter_ligne [13] empreinte [8] rechercher_par_id + [6] alea [14] generer_transactions [19] total_par_client + [7] categorie [15] indexer_par_id [20] total_par_jour + [10] categorie_credit (banque.c) [16] meilleure_periode [1] trier_intervalle (banque.c) + [9] categorie_debit (banque.c) [17] nb_clients_distincts [2] trier_par_montant + [5] comparer_transactions (banque.c) [18] nb_facons_rendre + [12] compter_paires_triees (banque.c) [4] nb_paires_opposees diff --git a/build/profil/profil_apres.txt b/build/profil/profil_apres.txt new file mode 100644 index 0000000..1eae81a --- /dev/null +++ b/build/profil/profil_apres.txt @@ -0,0 +1,126 @@ +Flat profile: + +Each sample counts as 0.01 seconds. + % cumulative self self total + time seconds seconds calls ms/call ms/call name +100.00 0.01 0.01 2 5.00 5.00 trier_intervalle + 0.00 0.01 0.00 817430 0.00 0.00 comparer_transactions + 0.00 0.01 0.00 149998 0.00 0.00 alea + 0.00 0.01 0.00 30000 0.00 0.00 categorie + 0.00 0.01 0.00 30000 0.00 0.00 rechercher_par_id + 0.00 0.01 0.00 15066 0.00 0.00 categorie_debit + 0.00 0.01 0.00 14934 0.00 0.00 categorie_credit + 0.00 0.01 0.00 6017 0.00 0.00 ajouter_ligne + 0.00 0.01 0.00 2 0.00 5.00 trier_par_montant + 0.00 0.01 0.00 1 0.00 0.00 compter_paires_triees + 0.00 0.01 0.00 1 0.00 0.00 empreinte + 0.00 0.01 0.00 1 0.00 0.00 generer_transactions + 0.00 0.01 0.00 1 0.00 0.00 indexer_par_id + 0.00 0.01 0.00 1 0.00 0.00 meilleure_periode + 0.00 0.01 0.00 1 0.00 0.00 nb_clients_distincts + 0.00 0.01 0.00 1 0.00 0.00 nb_facons_rendre + 0.00 0.01 0.00 1 0.00 5.00 nb_paires_opposees + 0.00 0.01 0.00 1 0.00 0.00 total_par_client + 0.00 0.01 0.00 1 0.00 0.00 total_par_jour + + + Call graph + + +granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01 seconds + +index % time self children called name + 119996 trier_intervalle [1] + 0.01 0.00 2/2 trier_par_montant [2] +[1] 100.0 0.01 0.00 2+119996 trier_intervalle [1] + 0.00 0.00 817430/817430 comparer_transactions [5] + 119996 trier_intervalle [1] +----------------------------------------------- + 0.00 0.01 1/2 nb_paires_opposees [4] + 0.00 0.01 1/2 main [3] +[2] 100.0 0.00 0.01 2 trier_par_montant [2] + 0.01 0.00 2/2 trier_intervalle [1] +----------------------------------------------- + +[3] 100.0 0.00 0.01 main [3] + 0.00 0.01 1/1 nb_paires_opposees [4] + 0.00 0.01 1/2 trier_par_montant [2] + 0.00 0.00 30000/30000 rechercher_par_id [8] + 0.00 0.00 30000/30000 categorie [7] + 0.00 0.00 6017/6017 ajouter_ligne [11] + 0.00 0.00 1/1 generer_transactions [14] + 0.00 0.00 1/1 indexer_par_id [15] + 0.00 0.00 1/1 nb_clients_distincts [17] + 0.00 0.00 1/1 total_par_client [19] + 0.00 0.00 1/1 total_par_jour [20] + 0.00 0.00 1/1 meilleure_periode [16] + 0.00 0.00 1/1 nb_facons_rendre [18] + 0.00 0.00 1/1 empreinte [13] +----------------------------------------------- + 0.00 0.01 1/1 main [3] +[4] 50.0 0.00 0.01 1 nb_paires_opposees [4] + 0.00 0.01 1/2 trier_par_montant [2] + 0.00 0.00 1/1 compter_paires_triees [12] +----------------------------------------------- + 0.00 0.00 817430/817430 trier_intervalle [1] +[5] 0.0 0.00 0.00 817430 comparer_transactions [5] +----------------------------------------------- + 0.00 0.00 149998/149998 generer_transactions [14] +[6] 0.0 0.00 0.00 149998 alea [6] +----------------------------------------------- + 0.00 0.00 30000/30000 main [3] +[7] 0.0 0.00 0.00 30000 categorie [7] + 0.00 0.00 15066/15066 categorie_debit [9] + 0.00 0.00 14934/14934 categorie_credit [10] +----------------------------------------------- + 0.00 0.00 30000/30000 main [3] +[8] 0.0 0.00 0.00 30000 rechercher_par_id [8] +----------------------------------------------- + 0.00 0.00 15066/15066 categorie [7] +[9] 0.0 0.00 0.00 15066 categorie_debit [9] +----------------------------------------------- + 0.00 0.00 14934/14934 categorie [7] +[10] 0.0 0.00 0.00 14934 categorie_credit [10] +----------------------------------------------- + 0.00 0.00 6017/6017 main [3] +[11] 0.0 0.00 0.00 6017 ajouter_ligne [11] +----------------------------------------------- + 0.00 0.00 1/1 nb_paires_opposees [4] +[12] 0.0 0.00 0.00 1 compter_paires_triees [12] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[13] 0.0 0.00 0.00 1 empreinte [13] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[14] 0.0 0.00 0.00 1 generer_transactions [14] + 0.00 0.00 149998/149998 alea [6] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[15] 0.0 0.00 0.00 1 indexer_par_id [15] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[16] 0.0 0.00 0.00 1 meilleure_periode [16] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[17] 0.0 0.00 0.00 1 nb_clients_distincts [17] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[18] 0.0 0.00 0.00 1 nb_facons_rendre [18] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[19] 0.0 0.00 0.00 1 total_par_client [19] +----------------------------------------------- + 0.00 0.00 1/1 main [3] +[20] 0.0 0.00 0.00 1 total_par_jour [20] +----------------------------------------------- + + +Index by function name + + [11] ajouter_ligne [13] empreinte [8] rechercher_par_id + [6] alea [14] generer_transactions [19] total_par_client + [7] categorie [15] indexer_par_id [20] total_par_jour + [10] categorie_credit (banque.c) [16] meilleure_periode [1] trier_intervalle (banque.c) + [9] categorie_debit (banque.c) [17] nb_clients_distincts [2] trier_par_montant + [5] comparer_transactions (banque.c) [18] nb_facons_rendre + [12] compter_paires_triees (banque.c) [4] nb_paires_opposees diff --git a/build/profil/profil_apres_repete.txt b/build/profil/profil_apres_repete.txt new file mode 100644 index 0000000..6501291 --- /dev/null +++ b/build/profil/profil_apres_repete.txt @@ -0,0 +1,135 @@ +Flat profile: + +Each sample counts as 0.01 seconds. + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 57.25 1.19 1.19 400 2.96 3.69 trier_intervalle + 14.01 1.48 0.29 163486000 0.00 0.00 comparer_transactions + 6.76 1.61 0.14 200 0.70 0.75 generer_transactions + 4.35 1.71 0.09 200 0.45 10.00 banque_main + 3.86 1.78 0.08 200 0.40 0.40 compter_paires_triees + 3.38 1.85 0.07 _init + 2.90 1.92 0.06 200 0.30 0.30 empreinte + 1.93 1.96 0.04 1203400 0.00 0.00 ajouter_ligne + 0.97 1.98 0.02 6000000 0.00 0.00 categorie + 0.97 2.00 0.02 200 0.10 0.10 nb_clients_distincts + 0.97 2.02 0.02 200 0.10 0.10 total_par_client + 0.48 2.02 0.01 29999600 0.00 0.00 alea + 0.48 2.04 0.01 6000000 0.00 0.00 rechercher_par_id + 0.48 2.04 0.01 3013200 0.00 0.00 categorie_debit + 0.48 2.06 0.01 200 0.05 0.05 indexer_par_id + 0.48 2.06 0.01 200 0.05 0.05 total_par_jour + 0.24 2.07 0.01 400 0.01 3.70 trier_par_montant + 0.00 2.07 0.00 2986800 0.00 0.00 categorie_credit + 0.00 2.07 0.00 200 0.00 0.00 meilleure_periode + 0.00 2.07 0.00 200 0.00 0.00 nb_facons_rendre + 0.00 2.07 0.00 200 0.00 4.10 nb_paires_opposees + + + Call graph + + +granularity: each sample hit covers 4 byte(s) for 0.48% of 2.07 seconds + +index % time self children called name + 0.09 1.91 200/200 main [2] +[1] 96.6 0.09 1.91 200 banque_main [1] + 0.00 0.82 200/200 nb_paires_opposees [5] + 0.00 0.74 200/400 trier_par_montant [3] + 0.14 0.01 200/200 generer_transactions [7] + 0.06 0.00 200/200 empreinte [10] + 0.04 0.00 1203400/1203400 ajouter_ligne [11] + 0.02 0.01 6000000/6000000 categorie [12] + 0.02 0.00 200/200 nb_clients_distincts [13] + 0.02 0.00 200/200 total_par_client [14] + 0.01 0.00 6000000/6000000 rechercher_par_id [16] + 0.01 0.00 200/200 indexer_par_id [18] + 0.01 0.00 200/200 total_par_jour [19] + 0.00 0.00 200/200 meilleure_periode [21] + 0.00 0.00 200/200 nb_facons_rendre [22] +----------------------------------------------- + +[2] 96.6 0.00 2.00 main [2] + 0.09 1.91 200/200 banque_main [1] +----------------------------------------------- + 0.00 0.74 200/400 nb_paires_opposees [5] + 0.00 0.74 200/400 banque_main [1] +[3] 71.5 0.01 1.48 400 trier_par_montant [3] + 1.19 0.29 400/400 trier_intervalle [4] +----------------------------------------------- + 23999200 trier_intervalle [4] + 1.19 0.29 400/400 trier_par_montant [3] +[4] 71.3 1.19 0.29 400+23999200 trier_intervalle [4] + 0.29 0.00 163486000/163486000 comparer_transactions [6] + 23999200 trier_intervalle [4] +----------------------------------------------- + 0.00 0.82 200/200 banque_main [1] +[5] 39.6 0.00 0.82 200 nb_paires_opposees [5] + 0.00 0.74 200/400 trier_par_montant [3] + 0.08 0.00 200/200 compter_paires_triees [8] +----------------------------------------------- + 0.29 0.00 163486000/163486000 trier_intervalle [4] +[6] 14.0 0.29 0.00 163486000 comparer_transactions [6] +----------------------------------------------- + 0.14 0.01 200/200 banque_main [1] +[7] 7.2 0.14 0.01 200 generer_transactions [7] + 0.01 0.00 29999600/29999600 alea [15] +----------------------------------------------- + 0.08 0.00 200/200 nb_paires_opposees [5] +[8] 3.9 0.08 0.00 200 compter_paires_triees [8] +----------------------------------------------- + +[9] 3.4 0.07 0.00 _init [9] +----------------------------------------------- + 0.06 0.00 200/200 banque_main [1] +[10] 2.9 0.06 0.00 200 empreinte [10] +----------------------------------------------- + 0.04 0.00 1203400/1203400 banque_main [1] +[11] 1.9 0.04 0.00 1203400 ajouter_ligne [11] +----------------------------------------------- + 0.02 0.01 6000000/6000000 banque_main [1] +[12] 1.4 0.02 0.01 6000000 categorie [12] + 0.01 0.00 3013200/3013200 categorie_debit [17] + 0.00 0.00 2986800/2986800 categorie_credit [20] +----------------------------------------------- + 0.02 0.00 200/200 banque_main [1] +[13] 1.0 0.02 0.00 200 nb_clients_distincts [13] +----------------------------------------------- + 0.02 0.00 200/200 banque_main [1] +[14] 1.0 0.02 0.00 200 total_par_client [14] +----------------------------------------------- + 0.01 0.00 29999600/29999600 generer_transactions [7] +[15] 0.5 0.01 0.00 29999600 alea [15] +----------------------------------------------- + 0.01 0.00 6000000/6000000 banque_main [1] +[16] 0.5 0.01 0.00 6000000 rechercher_par_id [16] +----------------------------------------------- + 0.01 0.00 3013200/3013200 categorie [12] +[17] 0.5 0.01 0.00 3013200 categorie_debit [17] +----------------------------------------------- + 0.01 0.00 200/200 banque_main [1] +[18] 0.5 0.01 0.00 200 indexer_par_id [18] +----------------------------------------------- + 0.01 0.00 200/200 banque_main [1] +[19] 0.5 0.01 0.00 200 total_par_jour [19] +----------------------------------------------- + 0.00 0.00 2986800/2986800 categorie [12] +[20] 0.0 0.00 0.00 2986800 categorie_credit [20] +----------------------------------------------- + 0.00 0.00 200/200 banque_main [1] +[21] 0.0 0.00 0.00 200 meilleure_periode [21] +----------------------------------------------- + 0.00 0.00 200/200 banque_main [1] +[22] 0.0 0.00 0.00 200 nb_facons_rendre [22] +----------------------------------------------- + + +Index by function name + + [9] _init [6] comparer_transactions (banque.c) [22] nb_facons_rendre + [11] ajouter_ligne [8] compter_paires_triees (banque.c) [5] nb_paires_opposees + [15] alea [10] empreinte [16] rechercher_par_id + [1] banque_main [7] generer_transactions [14] total_par_client + [12] categorie [18] indexer_par_id [19] total_par_jour + [20] categorie_credit (banque.c) [21] meilleure_periode [4] trier_intervalle (banque.c) + [17] categorie_debit (banque.c) [13] nb_clients_distincts [3] trier_par_montant diff --git a/build/temps_apres.txt b/build/temps_apres.txt new file mode 100644 index 0000000..9fd15ed --- /dev/null +++ b/build/temps_apres.txt @@ -0,0 +1,9 @@ +Mesures en secondes, gcc -std=c11 -Wall -Wextra -Wpedantic -O2 +Commande : build/banque, rapport écrit sur disque, temps réel (perf_counter). +Référence initiale, une exécution : 2.437683 +Exécution 1 : 0.010910 +Exécution 2 : 0.011111 +Exécution 3 : 0.010734 +Exécution 4 : 0.011242 +Exécution 5 : 0.010525 +Médiane après optimisation : 0.010910 diff --git a/build/validations_optimisations.csv b/build/validations_optimisations.csv new file mode 100644 index 0000000..e2b5f7e --- /dev/null +++ b/build/validations_optimisations.csv @@ -0,0 +1,11 @@ +fonction;empreinte;rapport_identique;temps_secondes;sha256_source +F1;ff68d260a7a58441;oui;0.941548;951d59178fee42db808259d2885504913006f2459be99a734619b322bf58011e +F2;ff68d260a7a58441;oui;0.939984;82088e5e81e3cd14319bbc3a3789ab76b3b1cdc54e92bf9e4811eadbce77d89a +F3;ff68d260a7a58441;oui;0.936668;e451e8d251c2194f63c631532d13b278242910977d7c597b9f27f5bbccb45522 +F4;ff68d260a7a58441;oui;0.925947;d8ee027417c8bb3a49184191a27fcd4bd903c85341368c8574152171fae6086b +F5;ff68d260a7a58441;oui;0.844800;3ce8f93c61a31cea27e3da75c1edec7bde6ef0255037e1e39aa95680552e3f81 +F6;ff68d260a7a58441;oui;0.643082;f3478bfeae098be4a35132c4e1b176c73ebe336134402419d1ddcc4607f3c45f +F7;ff68d260a7a58441;oui;0.382976;19a0887a08d835d94e0bf88491e9fa91810848eec0deb65cccdb7787e00a74f2 +F8;ff68d260a7a58441;oui;0.131249;3e46c05cc55725fb26aa2b1f1e16b8882e6000708654638ef7f165c80ef0a2e8 +F9;ff68d260a7a58441;oui;0.130851;98c643f223e4b721e01267431cb1bf8848a6772b6b3931ecd99731d0a7d0ffa8 +F10;ff68d260a7a58441;oui;0.010753;504e876d5868aece17c7379d2e92f2e8dc8c49042f300bfe0b1114a0d8b13680 diff --git a/notes.md b/notes.md index 23782be..4eacc59 100644 --- a/notes.md +++ b/notes.md @@ -1,16 +1,20 @@ -# Analyse des fonctions avec valeurs initiales +# Complexités avant et après l'étape 4 -| ID | Fonction | Calcule | Actuelle | Objectif | -|:---:|---|---|:---:|---| -| F1 | `trier_par_montant` | tri des transactions | `O(n²)` | `O(n log n)` | -| F2 | `nb_clients_distincts` | clients différents | `O(n²)` | `O(n + C)` | -| F3 | `total_par_client` | solde par client | `O(n * c)` | `O(n + C)` | -| F4 | `total_par_jour` | solde par jour | `O(n * J)` | `O(n + J)` | -| F5 | `meilleure_periode` | période la plus rentable | `O(J³)` | `O(J)` | -| F6 | `rechercher_par_id` | recherche par identifiant | `O(n)` | `O(1)` par requête | -| F7 | `nb_paires_opposees` | paires `a + b = 0` | `O(n²)` | `O(n log n)` | -| F8 | `nb_facons_rendre` | rendu de monnaie | `O(Sⁿ)` | `O(S × k)` | -| F9 | `categorie` | catégorie d'une transaction | M = 14 | `M ≤ 10` | -| F10 | `ajouter_ligne` | construction du rapport | `O(n)` | `O(L)` au total | +| ID | Fonction | Avant | Après (objectif atteint) | M avant → après | +|:---:|---|---|---|:---:| +| F1 | `trier_par_montant` | `O(n²)` | `O(n log n)` au pire cas | 6 → 3 | +| F2 | `nb_clients_distincts` | `O(n²)` | `O(n + C)` | 5 → 3 | +| F3 | `total_par_client` | `O(n × C)` | `O(n + C)` | 4 → 3 | +| F4 | `total_par_jour` | `O(n × J)` | `O(n + J)` | 4 → 3 | +| F5 | `meilleure_periode` | `O(J³)` | `O(J)` | 5 → 4 | +| F6 | `rechercher_par_id` | `O(n)` par requête | `O(1)` par requête, après indexation `O(n)` | 3 → 3 | +| F7 | `nb_paires_opposees` | `O(n²)` | `O(n log n)` | 4 → 3 | +| F8 | `nb_facons_rendre` | `O(2^(S + k))`, borne générale | `O(S × k)` pour `k ≥ 1` | 4 → 5 | +| F9 | `categorie` | `O(1)`, M = 14 | `O(1)`, M = 3 (auxiliaires : 5 et 7) | 14 → 3 | +| F10 | `ajouter_ligne` | `O(L²)` au total, lignes de taille bornée | `O(L)` au total | 2 → 2 | -`n` transactions · `C` clients · `J` jours · `S` somme · `k` pièces · `L` longueur du rapport · **F5, F7, F8** : les plus difficiles +`n` transactions · `C` clients · `J` jours · `S` somme · `k` pièces · `L` longueur du rapport. + +Justifications, mémoire auxiliaire, M des fonctions auxiliaires et validations +dans `COMPTE_RENDU.md`, section 4. Les constantes et le format du rapport +sont inchangés ; l'empreinte reste `ff68d260a7a58441` à chaque optimisation. diff --git a/tests/test_banque.c b/tests/test_banque.c index e0d86d2..9ff57f9 100644 --- a/tests/test_banque.c +++ b/tests/test_banque.c @@ -273,6 +273,35 @@ static void tester_categories(void) } } +static void tester_rapport(void) +{ + char rapport[4097] = ""; + char attendu[4097] = ""; + char *fin = rapport; + size_t taille = 0; + assert(ajouter_ligne(fin, "") == fin); + for (int i = 0; i < 200; i++) { + char ligne[20]; + int longueur_ligne = snprintf(ligne, sizeof(ligne), "ligne;%d\n", i); + assert(longueur_ligne > 0); + assert((size_t)longueur_ligne < sizeof(ligne)); + memcpy(attendu + taille, ligne, (size_t)longueur_ligne + 1); + taille += (size_t)longueur_ligne; + fin = ajouter_ligne(fin, ligne); + assert(fin == rapport + taille); + assert(*fin == '\0'); + assert(strcmp(rapport, attendu) == 0); + assert(ajouter_ligne(fin, "") == fin); + } + char longue_ligne[4097]; + memset(longue_ligne, 'x', sizeof(longue_ligne) - 1); + longue_ligne[sizeof(longue_ligne) - 1] = '\0'; + rapport[0] = '\0'; + fin = ajouter_ligne(rapport, longue_ligne); + assert(fin == rapport + sizeof(rapport) - 1); + assert(strcmp(rapport, longue_ligne) == 0); +} + int main(void) { tester_tri(); @@ -284,6 +313,7 @@ int main(void) tester_paires(); tester_monnaie(); tester_categories(); + tester_rapport(); puts("Tests banque : OK"); return 0; }