Quelle taille peut avoir un malloc en C?

J’ai un malloc en C qui est 26901 ^ 2 * sizeof (double)

Cela m’a fait penser à ce que la plus grande valeur peut être ici?

Aussi, aurais-je des problèmes pour définir une macro pour accéder à ce tableau 2D?

#define DN(i,j) ((int)i * ny + (int)j) 

Parce que cela semble ne pas fonctionner pour moi – ou du moins je ne suis pas sûr que ce soit le cas. Je n’arrive pas à comprendre comment faire de totalview une plongée sur une macro pour me dire ce que A [DN (indx, jndx)] examine réellement.

Observations

En supposant un allocateur typique, tel que celui utilisé par la glibc, voici quelques observations:

  1. Que la mémoire soit réellement utilisée ou non, la région doit être réservée de manière contiguë dans la mémoire virtuelle.
  2. Les plus grandes régions contiguës libres dépendent de l’utilisation de la mémoire des régions existantes et de la disponibilité de ces régions pour malloc .
  3. Les pratiques de mappage dépendent de l’architecture et du système d’exploitation. De plus, les appels système sous-jacents pour obtenir des régions de mémoire sont affectés par ces pratiques (par exemple, un appel de malloc via mmap pour acquérir des pages).

Expérience

Voici un programme simple pour allouer le plus grand bloc possible (comstackr avec gcc largest_malloc_size.c -Wall -O2 :

 #include  #include  #include  static void *malloc_wrap(size_t size) { void *p = malloc(size); if (p) { printf("Allocated %zu bytes from %p to %p\n", size, p, p + size); } else { printf("Failed to allocated %zu bytes\n", size); } return p; } int main() { size_t step = 0x1000000; size_t size = step; size_t best = 0; while (step > 0) { void *p = malloc_wrap(size); if (p) { free(p); best = size; } else { step /= 0x10; } size += step; } void *p = malloc_wrap(best); if (p) { pause(); return 0; } else { return 1; } } 

L’exécution du programme ci-dessus ( ./a.out ) sur mon Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux machine Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux obtient ce résultat:

  Allocated 2919235584 bytes from 0x9763008 to 0xb7763008 Allocated 2936012800 bytes from 0x8763008 to 0xb7763008 Failed to allocated 2952790016 bytes Failed to allocated 2953838592 bytes Failed to allocated 2953904128 bytes Failed to allocated 2953908224 bytes Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008 

Il s’agit d’une allocation d’environ 2800 Mo. Observer la correspondance pertinente à partir de /proc/[number]/maps :

  0804a000-0804b000 rw-p 00001000 08:07 3413394 /home/matt/anacrolix/public/stackoverflow/a.out 085ff000-b7600000 rw-p 00000000 00:00 0 [heap] b7600000-b7621000 rw-p 00000000 00:00 0 b7621000-b7700000 ---p 00000000 00:00 0 b7764000-b7765000 rw-p 00000000 00:00 0 b7765000-b78b8000 r-xp 00000000 08:08 916041 /lib/tls/i686/cmov/libc-2.11.1.so  bfc07000-bfc1c000 rw-p 00000000 00:00 0 [stack] 

Conclusion

Il semble que le segment de mémoire ait été étendu dans la zone située entre les données du programme et le code et les mappages de bibliothèques partagées, qui sont calés contre la limite d’espace mémoire utilisateur / kernel (évidemment 3G / 1G sur ce système).

Ce résultat suggère que l’espace maximum pouvant être alloué à l’aide de malloc est à peu près égal à:

  1. La région de l’espace utilisateur (3 Go dans l’exemple)
  2. Moins le décalage au début du tas (code de programme et données)
  3. Moins d’espace réservé pour la stack de threads principale
  4. Moins d’espace occupé par tous les mappés dans les bibliothèques partagées
  5. Enfin, la plus grande région contiguë pouvant être trouvée par l’appel système sous-jacent dans la région disponible pour le segment de mémoire (pouvant être fragmentée par d’autres mappages).

Remarques

En ce qui concerne les implémentations glibc et Linux, les extraits manuels suivants présentent un grand intérêt:

malloc

  Normally, malloc() allocates memory from the heap, and adjusts the size of the heap as required, using sbrk(2). When allocating blocks of mem‐ ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation allocates the memory as a private anonymous mapping using mmap(2). MMAP_THRESHOLD is 128 kB by default, but is adjustable using mal‐ lopt(3). 

mmap

  MAP_ANONYMOUS The mapping is not backed by any file; its contents are initial‐ ized to zero. 

Épilogue

Ce test a été réalisé sur un kernel x86. J’attendrais des résultats similaires d’un kernel x86_64, bien qu’avec des régions de mémoire beaucoup plus grandes. L’emplacement des mappages et le traitement des gros malloc peuvent varier selon les systèmes d’exploitation, de sorte que les résultats peuvent être très différents.

Cela dépend de votre implémentation malloc!

Selon Wikipedia, “Depuis la version v2.3, la bibliothèque GNU C (glibc) utilise un ptmalloc2 modifié, lui-même basé sur dlmalloc v2.7.0.” dlmalloc se réfère à la mise en œuvre du malloc de Doug Lea. La chose importante à noter dans cette implémentation est que les grands mallocs sont réalisés via la fonctionnalité de fichier mappé en mémoire du système d’exploitation, de sorte que ces blocs peuvent être très volumineux sans beaucoup de problèmes pour trouver un bloc contigu.

La réponse à la question malloc (dépend du système d’exploitation, ce que vous ne spécifiez pas), donc définissez-la comme suit:

 #define DN(i,j) ((int)i * ny + (int)j) 

n’est pas tout à fait sûr, car quelqu’un pourrait faire DN(a+b,c) qui se développe à

 ((int)a+b * ny + (int)c) 

ce qui n’est probablement pas ce que tu voulais. Donc, mettez beaucoup de parenthèses dedans:

 #define DN(i,j) ((int)(i) * ny + (int)(j)) 

pour voir ce que DN(indx,jndx) , il suffit d’ DN(indx,jndx) printf("%d\n",DN(indx,jndx));

Le paramètre size dans un appel à malloc est du type size_t, qui varie en fonction de l’implémentation. Voir cette question pour plus.

Cela m’a fait penser à ce que la plus grande valeur peut être ici?

26’901 ^ 2 = 723’663’801. Si votre double est de 8 octets, il est inférieur à 8 Go. Je ne vois absolument aucun problème à allouer autant de mémoire et mes applications en allouent régulièrement (sur des systèmes 64 bits) beaucoup plus. (La consommation de mémoire la plus importante que j’aie jamais vue était de 420 Go (sur les systèmes Solaris 10 numa avec 640 Go de RAM) avec le plus grand bloc continu d’environ 24 Go.)

La valeur la plus grande est difficile à identifier car elle dépend de la plate-forme: semblable aux systèmes 32 bits, elle dépend de la répartition espace utilisateur / espace kernel. Dans l’état actuel des choses, je pense qu’il faudrait d’abord atteindre la limite de la RAM physique réelle – avant d’atteindre la limite de ce que la libc peut allouer. (Et le kernel s’en moque, il étend souvent la mémoire virtuelle sans même se demander s’il y a suffisamment de RAM pour l’épingler.)

Le plus grand bloc de mémoire que vous pouvez demander à malloc() est la plus size_t valeur size_t – il s’agit de SIZE_MAX partir de . Le montant le plus important que vous pouvez demander avec succès dépend évidemment du système d’exploitation et de la configuration de la machine.

Votre macro n’est pas en sécurité. Il effectue le calcul de l’index avec une variable int , qui doit uniquement disposer d’une plage allant jusqu’à 32767. Toute valeur supérieure à cette valeur peut entraîner un débordement de signature, ce qui entraîne un comportement non défini. Vous size_t probablement mieux de faire le calcul en tant que size_t , car ce type doit pouvoir contenir tout index de tableau valide:

 #define DN(i, j) ((size_t)(i) * ny + (size_t)(j)) 

(Notez cependant que si vous fournissez des valeurs négatives pour i ou j , vous obtiendrez un index très en dehors des limites).