Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

fork() et free()

10 réponses
Avatar
JKB
Bonjour à tous,

Je viens de m'apercevoir d'un truc pas drôle du tout...
Considérons un processus de calcul travaillant sur des tableaux de
plus d'un Go. Ce processus dans certaines conditions fait un fork()
de lui-même (pour lancer des calculs parallèles sur plusieurs
processeurs). Toutes ses données sont donc dupliquées. Tout cela
fonctionne bien (syncronisation des processus, récupération des
données...), mais il me reste une histoire de gestion de la mémoire
qui me pose problème.

Extrait d'un top :

PID USERNAME LWP PRI NICE SIZE RES STATE TIME CPU COMMAND
28230 bertrand 1 30 0 1401M 539M run 0:38 57.59% serveur.rpl
25930 bertrand 3 59 0 1205M 2624K sleep 0:10 0.36% serveur.rpl
25681 bertrand 1 49 0 15M 1500K sleep 0:02 0.28% serveur.rpl
2377 bertrand 1 59 0 2472K 972K cpu 0:32 0.25% top
25671 bertrand 4 59 0 1205M 973M sleep 0:44 0.24% serveur.rpl
25932 bertrand 1 59 0 1205M 2132K sleep 0:01 0.24% serveur.rpl

Le processus 25671 est le père du 28230 et du 25930. Ce que je ne
comprends pas (mais j'ai dû rater quelque chose), c'est que je fais
dans le 25930 un free() des tableaux en question. Pourquoi est-ce
que je ne récupère pas l'utilisation de la mémoire. C'est assez
problématique d'autant que cette mémoire est effectivement utilisée
par le système :

Memory: 2046M real, 497M free, 5150M swap in use, 1548M swap free

En gros, je pensais avec ce free() récupérer un peu plus de 1 Go de
mémoire, ce qui n'est pas le cas. Pourquoi ? Les tableaux en
question sont déclarés en variables globales et statiques dans une
bibliothèque partagée :

static int *edgesIndex;

et alloués/libérés par le couple malloc()/free().

La machine est un Solaris 10, mais je ne suis pas sûr que cela
provienne du système...

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.

10 réponses

Avatar
espie
In article ,
JKB wrote:
Bonjour à tous,

Je viens de m'apercevoir d'un truc pas drôle du tout...
Considérons un processus de calcul travaillant sur des tableaux de
plus d'un Go. Ce processus dans certaines conditions fait un fork()
de lui-même (pour lancer des calculs parallèles sur plusieurs
processeurs). Toutes ses données sont donc dupliquées. Tout cela
fonctionne bien (syncronisation des processus, récupération des
données...), mais il me reste une histoire de gestion de la mémoire
qui me pose problème.

Extrait d'un top :

PID USERNAME LWP PRI NICE SIZE RES STATE TIME CPU COMMAND
28230 bertrand 1 30 0 1401M 539M run 0:38 57.59% serveur.rpl
25930 bertrand 3 59 0 1205M 2624K sleep 0:10 0.36% serveur.rpl
25681 bertrand 1 49 0 15M 1500K sleep 0:02 0.28% serveur.rpl
2377 bertrand 1 59 0 2472K 972K cpu 0:32 0.25% top
25671 bertrand 4 59 0 1205M 973M sleep 0:44 0.24% serveur.rpl
25932 bertrand 1 59 0 1205M 2132K sleep 0:01 0.24% serveur.rpl

Le processus 25671 est le père du 28230 et du 25930. Ce que je ne
comprends pas (mais j'ai dû rater quelque chose), c'est que je fais
dans le 25930 un free() des tableaux en question. Pourquoi est-ce
que je ne récupère pas l'utilisation de la mémoire. C'est assez
problématique d'autant que cette mémoire est effectivement utilisée
par le système :

Memory: 2046M real, 497M free, 5150M swap in use, 1548M swap free

En gros, je pensais avec ce free() récupérer un peu plus de 1 Go de
mémoire, ce qui n'est pas le cas. Pourquoi ? Les tableaux en
question sont déclarés en variables globales et statiques dans une
bibliothèque partagée :

static int *edgesIndex;

et alloués/libérés par le couple malloc()/free().

La machine est un Solaris 10, mais je ne suis pas sûr que cela
provienne du système...



Ca n'est plus vraiment un probleme de C. free() n'est pas garanti rendre
la memoire au systeme. Ca depend fortement de comment il est implemente
en termes de primitives, et aussi de si le systeme trouve des pages
contigues a rendre au systeme.

Il y a plein de choses a considerer: en particulier cette memoire est-elle
reellement utilisee ? les Unix modernes font souvent du Copy-on-Write, ce
qui fait que les pages sont partagees jusqu'a ce qu'une modification
survienne quelque part. Ca peut aussi dependre des parametres du
systeme, comme l'overcommit (est-ce que solaris decrete qu'il DOIT avoir
assez de place dans le swap pour tous tes process ?) ou des problemes
d'ulimit (ah tiens, vous avez potentiellement utilise trop de memoire).

Bref, je crains fort qu'il ne te faille regarder comment fonctionnent
les choses `en dessous' de malloc pour trouver une solution...
Avatar
JKB
Le 03-10-2008, ? propos de
Re: fork() et free(),
Marc Espie ?crivait dans fr.comp.lang.c :
In article ,
JKB wrote:
Bonjour à tous,

Je viens de m'apercevoir d'un truc pas drôle du tout...
Considérons un processus de calcul travaillant sur des tableaux de
plus d'un Go. Ce processus dans certaines conditions fait un fork()
de lui-même (pour lancer des calculs parallèles sur plusieurs
processeurs). Toutes ses données sont donc dupliquées. Tout cela
fonctionne bien (syncronisation des processus, récupération des
données...), mais il me reste une histoire de gestion de la mémoire
qui me pose problème.

Extrait d'un top :

PID USERNAME LWP PRI NICE SIZE RES STATE TIME CPU COMMAND
28230 bertrand 1 30 0 1401M 539M run 0:38 57.59% serveur.rpl
25930 bertrand 3 59 0 1205M 2624K sleep 0:10 0.36% serveur.rpl
25681 bertrand 1 49 0 15M 1500K sleep 0:02 0.28% serveur.rpl
2377 bertrand 1 59 0 2472K 972K cpu 0:32 0.25% top
25671 bertrand 4 59 0 1205M 973M sleep 0:44 0.24% serveur.rpl
25932 bertrand 1 59 0 1205M 2132K sleep 0:01 0.24% serveur.rpl

Le processus 25671 est le père du 28230 et du 25930. Ce que je ne
comprends pas (mais j'ai dû rater quelque chose), c'est que je fais
dans le 25930 un free() des tableaux en question. Pourquoi est-ce
que je ne récupère pas l'utilisation de la mémoire. C'est assez
problématique d'autant que cette mémoire est effectivement utilisée
par le système :

Memory: 2046M real, 497M free, 5150M swap in use, 1548M swap free

En gros, je pensais avec ce free() récupérer un peu plus de 1 Go de
mémoire, ce qui n'est pas le cas. Pourquoi ? Les tableaux en
question sont déclarés en variables globales et statiques dans une
bibliothèque partagée :

static int *edgesIndex;

et alloués/libérés par le couple malloc()/free().

La machine est un Solaris 10, mais je ne suis pas sûr que cela
provienne du système...



Ca n'est plus vraiment un probleme de C. free() n'est pas garanti rendre
la memoire au systeme. Ca depend fortement de comment il est implemente
en termes de primitives, et aussi de si le systeme trouve des pages
contigues a rendre au systeme.

Il y a plein de choses a considerer: en particulier cette memoire est-elle
reellement utilisee ? les Unix modernes font souvent du Copy-on-Write, ce



Visiblement, ce n'est pas le cas de Solaris. Les données sont
_toujours_ les mêmes, et l'occupation mémoire est celle de chacun
des tableaux multitpliés par le nombre de processus ! Il semble donc
copier a priori les données et ne pas utiliser un coy on write.

qui fait que les pages sont partagees jusqu'a ce qu'une modification
survienne quelque part. Ca peut aussi dependre des parametres du
systeme, comme l'overcommit (est-ce que solaris decrete qu'il DOIT avoir
assez de place dans le swap pour tous tes process ?) ou des problemes
d'ulimit (ah tiens, vous avez potentiellement utilise trop de memoire).



Je ne vois pas trop ce que l'overcommit vient faire là-dedans... Le
fait qu'il décrète qu'il doit y avoir assez de place doit être
conditionné à la taille réelle du processus, non ?

Bref, je crains fort qu'il ne te faille regarder comment fonctionnent
les choses `en dessous' de malloc pour trouver une solution...



Merci quand même, même si je ne suis pas plus avancé...

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
JKB
Le 03-10-2008, ? propos de
fork() et free(),
JKB ?crivait dans fr.comp.lang.c :
La machine est un Solaris 10, mais je ne suis pas sûr que cela
provienne du système...



Groumpf... Je viens de voir ceci :

After free() is executed, this space is made available for further allo-
cation by the application, though not returned to the system.

dans la page man de free de Solaris. La question est donc 'comment
passer à traver le truc ?', sachant que je ne peux pas me permettre
de garder ces trucs en mémoire...

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Xavier Roche
JKB a écrit :
dans la page man de free de Solaris. La question est donc 'comment
passer à traver le truc ?', sachant que je ne peux pas me permettre



C'est étonnant que free() ne libère effectivement pas la mémoire, étant
donné la taille du bloc. Les données du ps sont-elles bien interprétées ?

Vous pouvez sinon faire un appel direct à un mmap() anonyme (qui est
utilisé en fait par malloc()/free() sur pas mal d'OS dès lors que la
taille réservée est "grande")

void *addr = mmap(0, size,
/*prot*/PROT_READ | PROT_WRITE,
/*flags*/MAP_ANON | MAP_PRIVATE,
/*fd*/-1,
/*off*/0);
..
munmap(addr, size);
Avatar
JKB
Le 03-10-2008, ? propos de
Re: fork() et free(),
Xavier Roche ?crivait dans fr.comp.lang.c :
JKB a écrit :
dans la page man de free de Solaris. La question est donc 'comment
passer à traver le truc ?', sachant que je ne peux pas me permettre



C'est étonnant que free() ne libère effectivement pas la mémoire, étant
donné la taille du bloc. Les données du ps sont-elles bien interprétées ?



Je pense ;-)

Vous pouvez sinon faire un appel direct à un mmap() anonyme (qui est
utilisé en fait par malloc()/free() sur pas mal d'OS dès lors que la
taille réservée est "grande")

void *addr = mmap(0, size,
/*prot*/PROT_READ | PROT_WRITE,
/*flags*/MAP_ANON | MAP_PRIVATE,
/*fd*/-1,
/*off*/0);
..
munmap(addr, size);



Le munmap libère bien la mémoire et la rend au système
(contrairement au free sous Solaris). Je ne pense jamais à utiliser
ce truc... Par contre (j'ai cherché toute
la soirée hier...), pourquoi existe-t-il plusieurs bibliothèques
contenant malloc() sous Solaris ? J'ai trouvé un malloc dans la
libc, un autre dans libbsdmalloc (le free se comporte de la même
manière et ne rend pas la mémoire au système, seulement à
l'application, mais le malloc bouffe 20% de plus que le malloc de la
libc...) et encore deux ou trois trucs scabreux du même tonneau...

Merci pour tout,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
espie
In article ,
JKB wrote:
Le 03-10-2008, ? propos de
Re: fork() et free(),
Marc Espie ?crivait dans fr.comp.lang.c :
Il y a plein de choses a considerer: en particulier cette memoire est-elle
reellement utilisee ? les Unix modernes font souvent du Copy-on-Write, ce



Visiblement, ce n'est pas le cas de Solaris. Les données sont
_toujours_ les mêmes, et l'occupation mémoire est celle de chacun
des tableaux multitpliés par le nombre de processus ! Il semble donc
copier a priori les données et ne pas utiliser un coy on write.



Ca, tu n'en sais rien, en fait. confere plus bas.

qui fait que les pages sont partagees jusqu'a ce qu'une modification
survienne quelque part. Ca peut aussi dependre des parametres du
systeme, comme l'overcommit (est-ce que solaris decrete qu'il DOIT avoir
assez de place dans le swap pour tous tes process ?) ou des problemes
d'ulimit (ah tiens, vous avez potentiellement utilise trop de memoire).



Je ne vois pas trop ce que l'overcommit vient faire là-dedans... Le
fait qu'il décrète qu'il doit y avoir assez de place doit être
conditionné à la taille réelle du processus, non ?



Si tu es sur un systeme sans overcommit, l'occupation memoire annoncee
pour chacun des processus est forcement celle qui correspond a tout l'espace
memoire alloue: tu ne verras pas le copy-on-write et le partage de pages.
La seule facon *reelle* de savoir ce qu'il en est, ca serait de chronometrer
tes fork() pour verifier si la memoire est copiee ou pas (mais ca n'est pas
simple non plus, puisque meme avec du copy-on-write, tu auras quand meme
le temps de mise en place de la carte memoire de ton processus).
Avatar
espie
In article ,
JKB wrote:
Le 03-10-2008, ? propos de
Re: fork() et free(),
Xavier Roche ?crivait dans fr.comp.lang.c :
JKB a écrit :
dans la page man de free de Solaris. La question est donc 'comment
passer à traver le truc ?', sachant que je ne peux pas me permettre



C'est étonnant que free() ne libère effectivement pas la mémoire, étant
donné la taille du bloc. Les données du ps sont-elles bien interprétées ?



Je pense ;-)

Vous pouvez sinon faire un appel direct à un mmap() anonyme (qui est
utilisé en fait par malloc()/free() sur pas mal d'OS dès lors que la
taille réservée est "grande")

void *addr = mmap(0, size,
/*prot*/PROT_READ | PROT_WRITE,
/*flags*/MAP_ANON | MAP_PRIVATE,
/*fd*/-1,
/*off*/0);
..
munmap(addr, size);



Le munmap libère bien la mémoire et la rend au système
(contrairement au free sous Solaris). Je ne pense jamais à utiliser
ce truc... Par contre (j'ai cherché toute
la soirée hier...), pourquoi existe-t-il plusieurs bibliothèques
contenant malloc() sous Solaris ? J'ai trouvé un malloc dans la
libc, un autre dans libbsdmalloc (le free se comporte de la même
manière et ne rend pas la mémoire au système, seulement à
l'application, mais le malloc bouffe 20% de plus que le malloc de la
libc...) et encore deux ou trois trucs scabreux du même tonneau...

Merci pour tout,



Solaris est un systeme qui existe depuis longtemps. Il cherche a etre
compatible avec ses anciennes versions. La libbsdmalloc correspond sans
doute a l'implementation de malloc() qui existait en SunOS 4, des fois
qu'un vieux programme soit dependant de ses bugs... elle ne te concerne
sans doute pas.

Normalement en effet, sur les systemes modernes, les petits malloc sont
rarement rendus au systeme (etant reorganises sous formes d'allocation
de meme taille, et donc un peu disperses par rapport a l'utilisation qu'en
fait le code), et les gros malloc passent `a travers' et utilisent directement
mmap/munmap ou assimiles. MAIS il est fort possible que l'allocateur decide
de ne rien rendre au systeme pour gagner du temps: dans beaucoup de profils
d'utilisation, on va avoir une succession de malloc/free. et descendre
au niveau d'en-dessous (en particulier, rendre des pages au systeme) est
plus couteux que de ne rien faire, et juste attendre de tout rendre lorsque
le process meurt. Il se pourrait bien qu'il y ait un appel exotique quelque
part (style malloc_options() ou un truc du meme tonneau) qui te permette
de controler ce comportement de facon un peu plus fine...

Je n'ai pas regarde le code de solaris depuis qu'il a ete publie, et j'ai
peu bosse sur ce systeme recemment... quelqu'un de plus a jour la-dessus
pourra sans doute t'eclaircir...
Avatar
JKB
Le 04-10-2008, ? propos de
Re: fork() et free(),
Marc Espie ?crivait dans fr.comp.lang.c :
Solaris est un systeme qui existe depuis longtemps. Il cherche a etre
compatible avec ses anciennes versions. La libbsdmalloc correspond sans
doute a l'implementation de malloc() qui existait en SunOS 4, des fois
qu'un vieux programme soit dependant de ses bugs... elle ne te concerne
sans doute pas.

Normalement en effet, sur les systemes modernes, les petits malloc sont
rarement rendus au systeme (etant reorganises sous formes d'allocation
de meme taille, et donc un peu disperses par rapport a l'utilisation qu'en
fait le code), et les gros malloc passent `a travers' et utilisent directement
mmap/munmap ou assimiles. MAIS il est fort possible que l'allocateur decide
de ne rien rendre au systeme pour gagner du temps: dans beaucoup de profils
d'utilisation, on va avoir une succession de malloc/free. et descendre
au niveau d'en-dessous (en particulier, rendre des pages au systeme) est
plus couteux que de ne rien faire, et juste attendre de tout rendre lorsque
le process meurt. Il se pourrait bien qu'il y ait un appel exotique quelque
part (style malloc_options() ou un truc du meme tonneau) qui te permette
de controler ce comportement de facon un peu plus fine...



Je n'ai pas trouvé un tel truc. Je suis en train de remplacer mes
malloc()/munmap() par des mmap()/munmap() qui semblent mieux
convenir. Je récupère ma mémoire et cela me va bien ;-)

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Marc
JKB wrote:

Le munmap libère bien la mémoire et la rend au système
(contrairement au free sous Solaris). Je ne pense jamais à utiliser
ce truc... Par contre (j'ai cherché toute
la soirée hier...), pourquoi existe-t-il plusieurs bibliothèques
contenant malloc() sous Solaris ?



L'implémentation la plus efficace n'est pas toujours la même. La libc
cherche à minimiser l'espace utilisé, bsdmalloc est plus rapide mais
gaspille plus, mtmalloc est plus adapté au multi-thread, watchmalloc est
utile pour débugguer, fastmalloc suppose du mono-thread, etc. La version
la plus moderne semble être la libumem, mais elle semble utiliser mmap
(il doit y avoir une variable d'environnement pour choisir avec sbrk)
sans munmap.

Au passage, ça serait pas mal d'annoncer dans un groupe que vous avez
posté quelque chose de lié dans un autre groupe, avec une référence au
thread.
Avatar
JKB
Le 06-10-2008, ? propos de
Re: fork() et free(),
Marc ?crivait dans fr.comp.lang.c :
JKB wrote:

Le munmap libère bien la mémoire et la rend au système
(contrairement au free sous Solaris). Je ne pense jamais à utiliser
ce truc... Par contre (j'ai cherché toute
la soirée hier...), pourquoi existe-t-il plusieurs bibliothèques
contenant malloc() sous Solaris ?



L'implémentation la plus efficace n'est pas toujours la même. La libc
cherche à minimiser l'espace utilisé, bsdmalloc est plus rapide mais
gaspille plus, mtmalloc est plus adapté au multi-thread, watchmalloc est
utile pour débugguer, fastmalloc suppose du mono-thread, etc. La version
la plus moderne semble être la libumem, mais elle semble utiliser mmap
(il doit y avoir une variable d'environnement pour choisir avec sbrk)
sans munmap.



libumem ne libère pas plus la mémoire que les autres
implantations...

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.