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

Allouer un très gros espace mémoire

8 réponses
Avatar
kathan
Bonjour,

En TP de complexit=E9 je dois effectuer des programmes ayant diff=E9rents
co=FBts O(n), O(log n), O(n!)... et voir pour quelle taille n il
tourneront pendant 3min. Pour le prog en log n, j'ai fait la recherche
d'un nombre dans un tableau tri=E9. Le probl=E8me c'est que j'arrive pas =
=E0
le faire tourner pendant 3min, en effet la taille n du tableau qu'il
faudrait est tellement grande que j'arrive pas =E0 l'allouer.
J'ai pas fait le calcul mais je suppose que c'est impossible d'allouer
la taille n=E9cessaire.
Cependant, en testant un peu, j'ai vu que sur mon pc, intel 32-bit,
sous linux, j'arrive =E0 allouer qu'environ 400Mo de m=E9moire (avec
malloc) avant que =E7a p=E8te. Or il parait que normalement on peut
allouer jusqu'=E0 3Go de mem sous linux 32-bit.
Donc j'aimerai savoir si vous avez un bout de code qui permettrait
d'allouer jusqu'=E0 3Go de m=E9moire - ou au moins un peu plus que 400Mo.

Merci.

8 réponses

Avatar
Nicolas George
"kathan" wrote in message
:
Cependant, en testant un peu, j'ai vu que sur mon pc, intel 32-bit,
sous linux, j'arrive à allouer qu'environ 400Mo de mémoire (avec
malloc) avant que ça pète. Or il parait que normalement on peut
allouer jusqu'à 3Go de mem sous linux 32-bit.
Donc j'aimerai savoir si vous avez un bout de code qui permettrait
d'allouer jusqu'à 3Go de mémoire - ou au moins un peu plus que 400Mo.


Ce n'est pas exactement ça. Il y a deux choses qui peuvent limiter la
mémoire que tu peux allouer : la mémoire virtuelle disponible, et l'espace
d'adressage.

La mémoire virtuelle, c'est le total de la mémoire physique plus le swap. Le
noyau ne peut pas allouer plus que ce total à l'ensemble des processus du
système. Il fait plus ou moins de sur-réservation : promettre plus à un
processus qu'il ne peut effectivement tenir compte tenu des autres
processus, dans l'espoir que ça passera au final. Mais il ne fait à ma
connaissance jamais de sur-réservation d'un processus sur lui-même. Ce qui
fait que si un processus réclame plus de mémoire que le total de mémoire
virtuelle, ça ne passera jamais.

L'espace d'adressage, c'est l'ensemble des adresses que le processeur peut
manipuler. Comme Linux ne fait pas usage de la segmentation, sur un
processeur 32 bits, cet espace d'adressage est de 2^32 octets, soit 4 Go.
Sur un processeur de type x86_64, il semble être de 2^64, mais est en fait
de seulement 2^48 (mais d'une manière qui permet les extensions futures sans
accrocs). Un processus ne peut pas manipuler plus que ça de mémoire à un
instant donné, et cet espace d'adressage est partagé entre différents
composants du programme :

- le code du binaire lui-même, vers le tout début de l'espace d'adressage ;

- le « tas », zone de mémoire extensible dédiée aux petites allocations de
mémoire, qui grossit à la demande (c'est l'appel système brk) commence
ensuite, et a environ 1 Go d'espace devant lui avant la zone suivante ;

- les mappings mémoire, des zones correspondant à des fichiers sur disque
(chargés au besoin ; en particulier, les bibliothèques partagées sont de
ce type), à des zones anonymes (pour les grosses allocations dynamique), à
la mémoire partagée, commencent vers 0x40000000 (sur les x86) ;

- la pile, commence un peu avant 0xC0000000 et croît automatiquement vers le
bas, ce qui laisse 2 Go au total pour la pile et les mappings ;

- l'espace d'adresses du noyau, qui sert lorsque le noyau s'exécute pour le
compte du processus pour accéder à des zones de mémoire étrangères,
commence à 0xC0000000 et va jusqu'à la fin, soit 1 Go.

On peut consulter la structure d'un processus donné dans le fichier virtuel
/proc/$PID/maps. C'est assez instructif.

À la vue de ce que j'ai expliqué, un processus peut allouer un peu moins
d'1 Go dans son tas, et 2 Go dans ses mappings, soit presque 3 Go au total
dans deux allocations contiguës.

Voici un petit programme qui illustre le tout :

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define STEP 4096 /* must be a power of 2 */

static void
cat_file(const char *file)
{
FILE *f;
int c;

if((f = fopen(file, "r")) == NULL) {
perror(file);
return;
}
printf("%s:n", file);
while((c = fgetc(f)) != EOF)
putchar(c);
fclose(f);
putchar('n');
}

int
main(void)
{
size_t min, max;
size_t mid;
void *t;

min = STEP;
max = SIZE_MAX & ~(STEP - 1);
mid = max;
while(max - min > 0) {
printf("Trying %llu: ", (unsigned long long)mid);
if((t = malloc(mid)) == NULL) {
printf("failed.n");
max = mid - STEP;
} else {
free(t);
printf("success.n");
min = mid;
}
mid = (max / 2 + min / 2 + STEP / 2) & ~(STEP - 1);
}
printf("Max success: %llun", (unsigned long long)min);
printf("Min failure: %llun", (unsigned long long)(max + STEP));

mid = (min - min / 8) & ~(STEP - 1);
printf("Allocating %llu: ", (unsigned long long)mid);
if((t = malloc(mid)) == NULL) {
printf("failedn");
} else {
printf("success, address = %pn", t);
cat_file("/proc/self/maps");
cat_file("/proc/meminfo");
}

return(0);
}

Avatar
Matthieu Moy
Blaise Potard writes:

for (i = 0; i < 100000; i++)
if (!malloc(size)) break;
printf("Planté à : %d Mon", i);
^^


Pourquoi _M_o ? C'est pas juste des octets ?

--
Matthieu

Avatar
Nicolas George
Matthieu Moy wrote in message :
Pourquoi _M_o ? C'est pas juste des octets ?


Tu as coupé le :

const int size = 1000000;

qui ne représente pas exactement un méga-octet, mais presque.

Avatar
Matthieu Moy
Nicolas George <nicolas$ writes:

Tu as coupé le :

const int size = 1000000;


Oups. Fatigué, moi ...

--
Matthieu

Avatar
Blaise Potard
Bonjour,

En TP de complexité je dois effectuer des programmes ayant différents
coûts O(n), O(log n), O(n!)... et voir pour quelle taille n il
tourneront pendant 3min. Pour le prog en log n, j'ai fait la recherche
d'un nombre dans un tableau trié. Le problème c'est que j'arrive pas à
le faire tourner pendant 3min, en effet la taille n du tableau qu'il
faudrait est tellement grande que j'arrive pas à l'allouer.
J'ai pas fait le calcul mais je suppose que c'est impossible d'allouer
la taille nécessaire.
Cependant, en testant un peu, j'ai vu que sur mon pc, intel 32-bit,
sous linux, j'arrive à allouer qu'environ 400Mo de mémoire (avec
malloc) avant que ça pète. Or il parait que normalement on peut
allouer jusqu'à 3Go de mem sous linux 32-bit.
Donc j'aimerai savoir si vous avez un bout de code qui permettrait
d'allouer jusqu'à 3Go de mémoire - ou au moins un peu plus que 400Mo.


Précise un peu quel langage tu utilises ! Ici il s'agit de C, vu que tu
as mis le même message sur fclc. Bon, effectivement linux permet
d'_adresser_ 3Go de mémoire mais, même si tu peux adresser jusqu'à 3Go
de mémoire par processus, ça ne veut pas dire que tu peux en _allouer_
autant, et surtout pas en un seul morceau. Ça dépend aussi de la mémoire
vive que tu as dans ta machine et de la taille de ton swap. Si tu as
suffisamment de mémoire vive, utilise des "petits" blocks de quelques
mégas, et normalement tu n'as aucun problème à atteindre les 3Go. En
fait, par les mécanismes magiques de pagination, il est même possible
d'allouer cet espace même si tu n'en disposes pas physiquement (mais tu
ne pourra pas forcément t'en servir, et tu risque de te faire ton
logiciel). Par contre, ne compte pas faire un seul gros tableau de
3Go...

#include <stdio.h>
#include <stdlib.h>
int main() {
const int size = 1000000;
int i;
for (i = 0; i < 100000; i++)
if (!malloc(size)) break;
printf("Planté à : %d Mon", i);
return 0;
}

fleury% ./a.out
Planté à : 100000 Mo

(en fait, il n'a pas planté, il a juste atteint la valeur maximale que
j'avais fixé).

À comparer à :

/* Deuxième version */

#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned char *a;
const int size = 1000000;
int i, j;
for (i = 0; i < 100000; i++) {
if (!(a = malloc(size))) break;
printf("Arrivé à : %d Mon", i);
for (j = 0; j < size; j++) a[j] = 1;
}
printf("Planté à : %d Mon", i);
return 0;
}

fleury% ./a.out
Arrivé à : 1 Mo
Arrivé à : 2 Mo
...
Arrivé à : 4026 Mo
zsh: killed ./a.out

P.S.: tu peux constater que j'ai une machine 64-bits, tu n'atteindras
donc pas les mêmes valeurs que moi.

Avatar
Pascal Hambourg
Salut,


const int size = 1000000;

qui ne représente pas exactement un méga-octet, mais presque.


Sisi. Je suppose que tu voulais dire "pas exactement un mébi-octet". ;-)

Avatar
Nicolas George
Pascal Hambourg wrote in message <erf8dv$2ja9$:
Sisi. Je suppose que tu voulais dire "pas exactement un mébi-octet". ;-)


Non, je veux dire que ceux qui cautionnes ces aberrations sont des abrutis.

Avatar
Blaise Potard

La mémoire virtuelle, c'est le total de la mémoire physique plus le swap. Le
noyau ne peut pas allouer plus que ce total à l'ensemble des processus du
système. Il fait plus ou moins de sur-réservation : promettre plus à un
processus qu'il ne peut effectivement tenir compte tenu des autres
processus, dans l'espoir que ça passera au final. Mais il ne fait à ma
connaissance jamais de sur-réservation d'un processus sur lui-même. Ce qui
fait que si un processus réclame plus de mémoire que le total de mémoire
virtuelle, ça ne passera jamais.


Ben, en l'occurence il semble que si. En tout cas, quand j'ai essayé
tout à l'heure, il accepté de m'allouer une petite centaine de Go sans
broncher (alors que je n'ai guère qu'un peu plus de 4Go de mémoire
virtuelle disponible). Il a juste fait la tronche quand j'ai voulu m'en
servir...