Questions sur la mémoire

Questions sur la mémoire - C - Programmation

Marsh Posté le 10-06-2004 à 22:46:42    

Salut,
 
Sous Linux, en C, j'analyse les programmes suivants avec la commande top pour savoir combien ils utilisent en RAM.
Je mets volontairement des variables énormes. Les scanf permettent juste de diviser en étapes.
Le but est de savoir quelle méthode consomme le moins de ressources, et comment est géré tout çà.
 
ESSAI 1 : variables locales
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. extern int test1();
  4. extern int test2();
  5. extern int test3();
  6. int main(void){
  7.   int buf;
  8.   scanf("%d",&buf);
  9.   test1();
  10.   test2();
  11.   test3();
  12.   scanf("%d",&buf);
  13.   return 0;
  14. }


 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int test1(){
  4.   char message[50000000]="";
  5.   strcpy(message,"salut1" );
  6.   return 0;
  7. }
  8. int test2(){
  9.   char message[50000000]="";
  10.   strcpy(message,"salut2" );
  11.   return 0;
  12. }
  13. int test3(){
  14.   char message[50000000]="";
  15.   strcpy(message,"salut3" );
  16.   return 0;
  17. }


 
On compile tout çà :
 
gcc -c essai1.c
gcc -c libessai1.c
gcc -o essai1 essai1.o libessai1.o

 
Le programme essai1 fait 14126 octets.
 
Résultat :
- avant appels aux fonctions test1, test2 et test3 : 0.1% MEM occupé
- après appels aux fonctions test1, test2 et test3 : 19.2% MEM occupé
 
Question : pourquoi la mémoire des variables locales (message) n'est pas détruite après les appels des fonctions ?
 
 
ESSAI 2 : variable globale
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. extern int test1();
  4. extern int test2();
  5. extern int test3();
  6. extern char message[50000000];
  7. int main(void){
  8.   int buf;
  9.   scanf("%d",&buf);
  10.   test1();
  11.   test2();
  12.   test3();
  13.   scanf("%d",&buf);
  14.   printf("MESSAGE vaut : %s\n",message);
  15.   return 0;
  16. }


 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. char message[50000000]="";
  4. int test1(){
  5.   strcpy(message,"salut1" );
  6.   return 0;
  7. }
  8. int test2(){
  9.   strcpy(message,"salut2" );
  10.   return 0;
  11. }
  12. int test3(){
  13.   strcpy(message,"salut3" );
  14.   return 0;
  15. }


 
On compile tout çà :
 

gcc -c essai2.c
gcc -c libessai2.c
gcc -o essai2 essai2.o libessai2.o

 
Le programme essai2 fait 50014000 octets.
 
Résultat :
- avant appels aux fonctions test1, test2 et test3 : 0.1% MEM occupé
- après appels aux fonctions test1, test2 et test3 : 0.1% MEM occupé
 
Questions :
- pourquoi essai2 et libessai2.o sont si gros ?
- pourquoi on n'utilise toujours que 0.1% de la MEM, alors que la valeur de message est bien disponible lors du printf ?
 
Merci !


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 10-06-2004 à 22:46:42   

Reply

Marsh Posté le 10-06-2004 à 23:13:29    

Pour le 1, je ne sais pas, c'est peut-être dû à la gestion RAM /swap par Linux (?).
 
Pour la 2, tu fais une initialisation statique dans la librairie. La chaine est réservée et inscrite directement dans le code de la librairie (soit 5Mo + 14ko), qui est linkée statiquement avec ton programme principal et lue et recopiée en RAM au démarrage.
Peut-être qu'à ce moment là, il ne lit et n'alloue le tableau que jusqu'au premier '\0', càd l'octet 0. :sweat:
 
C'est une question pour Taz, ça.


Message édité par el muchacho le 10-06-2004 à 23:22:25
Reply

Marsh Posté le 10-06-2004 à 23:22:28    

OK pour la 2, mais si la chaine est chargée en RAM au démarrage, pourquoi je n'ai que 0.1% qui est utilisée ? Je devrais avoir 19.2% (soit 50Mo) non ?


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 10-06-2004 à 23:28:42    

Ben c'est pas clair. Je pense qu'il ne copie que jusqu'au premier '\0', mais pourquoi il n'allouerait pas les 50 Mo... j'avoue que je sèche.
 
T'as regardé si la swap n'avait pas grossi ?

Reply

Marsh Posté le 10-06-2004 à 23:38:16    

Je crois ne pas avoir vu de modification de la swap.
Je vérifierai çà demain matin.
 
Merci & Bonne nuit !


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 10-06-2004 à 23:40:26    

fait un memset de ta zone allouée et ça devrait gonfler :D

Reply

Marsh Posté le 10-06-2004 à 23:44:12    

Déconne pas, çà je l'ai fait, effectivement çà a gonflé à 19.2%, mais je comprends pas la logique malgré tout.


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 10-06-2004 à 23:59:23    

c'est que sous linux tant que la mémoire n'est pas réellement utilisée elle reste "dispo" comme ça quand tu fais la brute avec des gros mallocs pas de gaspillage.

Reply

Marsh Posté le 11-06-2004 à 00:02:45    

Donc (dans le cas 2), c'est en fait alloué dynamiquement. A ce moment là, pourquoi avoir besoin de créer un exécutable énorme ?


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 11-06-2004 à 00:08:36    

Oui.
 
Peut-être que le compilo considère que si tu lui passes la longueur, c'est que tu as une bonne raison.
 
Sinon, tu aurais dû en principe écrire :
char message[] = "Coucou";
 
Auquel cas, le code n'aurait fait que 7 octets supplémentaires.

Reply

Marsh Posté le 11-06-2004 à 00:08:36   

Reply

Marsh Posté le 11-06-2004 à 00:13:27    

Oki çà s'éclaire. Cette fois-ci je vais me pieuter et je verrais çà demain.
Bonne nuit.


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 11-06-2004 à 00:16:53    

el muchacho a écrit :

Oui.
 
Peut-être que le compilo considère que si tu lui passes la longueur, c'est que tu as une bonne raison.
 
Sinon, tu aurais dû en principe écrire :
char message[] = "Coucou";
 
Auquel cas, le code n'aurait fait que 7 octets supplémentaires.


 
voila :jap:
 

  • le compilo fera dans le cas du statique une réservation de place où il mettra la variable (et tt son contenu)
  • ou bien il fait appel à une fonction de bibliothèque (malloc) qui réservera la mémoire. Pas besoin "d'embarquer" tout le "contenu" de la variable.


Reply

Marsh Posté le 11-06-2004 à 00:30:14    

alloue dynamiquement pour le 1 ?
 
je pensait que cetait plutot la pile dans ce cas qui etait utilisee...
 
dailleur ca metonne qu'on a pas un debordement de pile avec des tableau pareils
 
 
alloc dynamic c surttout a coup de malloc ou new non ?


Message édité par red faction le 11-06-2004 à 13:58:51
Reply

Marsh Posté le 11-06-2004 à 10:03:12    

Pour le 2 ok.
Pour le 1 par contre, c'est pas très clair.
 
En fait le but de tout çà : les fonctions test1(), test2() et test3() doivent remplir un buffer à partir de leurs paramètres. Leurs paramètres sont des entiers et des chaines de longueur indéterminée, par exemple :
 

Code :
  1. int test1(char *chaine, int *entier, char *chaine2){
  2.   char chformat[]="Salut %s çà va %s";
  3.   ......
  4.   sprintf(buf,chformat,chaine,chaine2);
  5.   ......
  6.   bufferiser(buf);
  7.   ......
  8. }


 
Ce buffer sera vidé et envoyé par TCP/IP dans une autre fonction finale.
 
Alors je me demandais quelle méthode on doit prendre pour optimiser au maximum en terme de mémoire et d'occupation processeur :
 
- déclarer la variable locale buf en tant que chaine de longueur fixe ?
- déclarer la variable locale buf en tant que pointeur et l'allouer dynamiquement ?
et
- déclarer le buffer global en tant que variable globale de longueur fixe ?
- déclarer le buffer global en tant que pointeur alloué dynamiquement ?
 
ou encore autre chose ?
 
Merci !


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 11-06-2004 à 10:29:42    

Pour le 1, essaye voir ce que donne la mémoire après un :  
message = "";
Ca m'étonnerait que ça retombe à 0, mais on ne sait jamais.
A mon avis, c'est une autre optimisation de la gestion mémoire de Linux, qui ne désallouerait réllement que s'il en a l'intérêt ou au bout d'un certains temps, un peu à la manière d'un garbage collector.
 
Pour ta question 2, je n'échangerais pas qualité de code pour un gain de vitesse la plupart du temps négligeable dans cette application (vu que de toute façon, les temps de latence réseau seront sans doute limitatifs).
 
Donc ma réponse est :
variable locale buf en tant que chaine de longueur fixe (tu introduis une contrainte supplémentaires sur la longueur des chaines) ou dynamique, selon tes besoins mémoire.
Après ça, si tu tiens vraiment à faire de l'optimisation vitesse (à vérifier avec des tests) et que tu connais d'avance le format de ta chaîne, n'utilise pas sprintf, qui est est un parseur, donc une usine à gaz (jette un oeil à son code), et construis-la à la main.


Message édité par el muchacho le 11-06-2004 à 10:35:31
Reply

Marsh Posté le 11-06-2004 à 12:59:53    

Ok.
 
Mais si je n'utilise pas sprintf, je serais réduit à faire une série de strcat, sachant que certaines fonctions ont jusqu'à 8 paramètres, çà ferait beaucoup de strcat, c'est pas mieux qu'un seul sprintf... non ?
Et concernant la variable globale, statique ou dynamique ?


Message édité par jeremy le 11-06-2004 à 13:00:14

---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 11-06-2004 à 14:36:04    

Il y a de grosses chances que si, vu que chacun des paramètres est parsé. Mais pour tes deux questions, la seule réponse valable est : à toi de voir, teste pour te faire ta propre idée.

Reply

Marsh Posté le 11-06-2004 à 14:56:52    

Au passage, scanf("%d",&buf); peut être remplacé par scanf("%*d" );
Pour le 1, je dirais qu'il s'agit de variables crées sur la pile, donc ton programme va essayer de réserver 50Mo sur la pile. Au départ, sa pile est beaucoup plus petite => exception, stack overflow, Linux réagit en faisant grossir la pile, et tout repart comme si de rien n'était. Sauf que ton programme possède une pile de 50Mo. Je trouve normal que l'OS fasse grossir la pile et ne la fasse pas rétrécir.
Moralité : allouer de grosses variables sur la pile c'est mal.
La taille initiale de la pile est normalement réglable dans les options de compialtion (linker).


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 11-06-2004 à 15:20:17    

Donc si c'est mal, qu'est-ce que tu me conseilles :
 
- variable locale : taille fixe ou pointeur+malloc ? (moi je dirais dynamique)
- variable globale (buffer général) : taille fixe ou pointeur+malloc ?
 
Merci !


---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 11-06-2004 à 15:36:49    

Pas de grosse variables sur la pile.
Gros tableaux locaux : malloc.
En global/static on s'en fout, c'est pas sur la pile.
Dans le cas 2, change char message[50000000]=""; en char message[50000000]; (non initialisé) et ton exe maigrit de 50Mo.
D'une manière générale j'aime pas les tailles fixes.
En informatique, y'a toujours des limites. Mettre une grosse taille repousse les limites, mais ne résoud pas le problème qu'elles peuvent être atteintes.
 
edit : petite coquille


Message édité par HelloWorld le 11-06-2004 à 15:37:13

---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 11-06-2004 à 15:55:46    

le mieux si ta un gros tableau a utiliser c de lallouer qd ten a besoin a coup de new ou malloc...
 
edit : grilled de 20 min jetait parti bouffer et jai pas valider le msg


Message édité par red faction le 11-06-2004 à 15:56:34
Reply

Marsh Posté le 11-06-2004 à 16:19:47    

Pas assez rapide petit scarabé !


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 11-06-2004 à 16:44:32    

Bon.
Après quelques tests (sur programme réel en exploitation), je me dis qu'avoir des variables locales fixes (pas 50Mo je vous rassure :) : une chaine de 20000 octets çà vous parait gros ? ), c'est négligeable. Et surtout, sur un gros traitement, c'est plus rapide que de s'amuser à faire des malloc, étant donbné que pour allouer dynamiquement, il faut s'amuser à calculer la taille réelle de la chaine à chaque fois à coup de strlen.
 
Je crois donc que je vais privilégier les temps de réponse par rapport à un gain de mémoire qui serait, je pense, négligeable.
 
Merci à tous.
 
Je vous invite à lire : http://ilay.org/yann/articles/mem/


Message édité par jeremy le 11-06-2004 à 18:24:36

---------------
Savoir c'est vivre, et maintenir dans l'ignorance, c'est presque un homicide.
Reply

Marsh Posté le 13-06-2004 à 22:40:41    

Moui.
Mais un tableau de taille fixe a une taille...fixe.
Que faire si ton tableau est trop petit ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 13-06-2004 à 22:44:52    

HelloWorld a écrit :

Moui.
Mais un tableau de taille fixe a une taille...fixe.


mais un tableau a toujours une taille fixe

HelloWorld a écrit :


Que faire si ton tableau est trop petit ?

DTC. s'il existe des cas ou ton tableau va être trop petit il faut déjà le détecter et prévénir tout débordements, passer en dynamique si tu n'es pas capable de prévoir au pire tes besoins

Reply

Marsh Posté le 13-06-2004 à 22:51:44    

Hum... tu as lu tout le post ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 13-06-2004 à 22:54:38    

NON :D
 
ma réponse initiale c'était « plop, ça gaze »

Reply

Marsh Posté le 13-06-2004 à 22:58:04    

« char chformat[]="Salut %s çà va %s"; »
 
fait gaffe là, tu cause 36 relocations pour rien. fais comme ça, fait un size ./a.out. maitenant rajoute un static const, refait un size, et tu vas voir !

Reply

Marsh Posté le 13-06-2004 à 23:39:37    

HelloWorld a écrit :

Moui.
Mais un tableau de taille fixe a une taille...fixe.
Que faire si ton tableau est trop petit ?


 
En plus c'est dangereux, parce que l'allocation statique de tableaux au debut d'un programme est brevete.  :lol: (Si si, c'est pas une blague, en plus ! :sweat: )
 
" Statically allocating an initial amount of memory when a program is first loaded according to a size value contained in the program header. [#5,247,674]."
http://www.base.com/software-patents/examples.html

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed