¿Qué tan grande puede ser un malloc en C?

Tengo un malloc en C que es 26901 ^ 2 * sizeof (doble)

Esto me hizo pensar ¿cuál es el mayor valor aquí?

Además, ¿tendría algún problema para definir una macro para acceder a esta matriz 2D?

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

Porque parece que esto no funciona para mí, o al menos no estoy seguro de que sea así. No puedo entender cómo hacer una inmersión total en una macro para decirme qué es lo que A [DN (indx, jndx)] está mirando en realidad.

Observaciones

Suponiendo un asignador típico, como el que usa glibc, hay algunas observaciones:

  1. Independientemente de si la memoria se utiliza o no, la región debe reservarse contiguamente en la memoria virtual.
  2. Las regiones contiguas libres más grandes dependen del uso de la memoria de las regiones de memoria existentes y de la disponibilidad de esas regiones para malloc .
  3. Las prácticas de mapeo dependen de la architecture y el sistema operativo. Además, las llamadas subyacentes al sistema para obtener regiones de memoria se ven afectadas por estas prácticas (como la llamada malloc a mmap para adquirir páginas).

Experimentar

Aquí hay un progtwig simple para asignar el bloque más grande posible (comstackr con 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; } } 

Ejecutando el progtwig anterior ( ./a.out ) en mi Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux máquina Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux obtiene este resultado:

  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 

Esta es una asignación de exactamente 2800MiB. Observando el mapeo relevante 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] 

Conclusión

Parece que el montón se ha expandido en el área entre los datos del progtwig y el código, y las asignaciones de bibliotecas compartidas, que se ajustan al límite del espacio de memoria del usuario / kernel (obviamente 3G / 1G en este sistema).

Este resultado sugiere que el espacio máximo asignable usando malloc es aproximadamente igual a:

  1. La región de espacio de usuario (3GB en el ejemplo)
  2. Menos el desplazamiento al inicio del montón (código de progtwig y datos)
  3. Menos espacio reservado para la stack principal de hilos
  4. Menos espacio ocupado por todas las asignadas en bibliotecas compartidas
  5. Finalmente, la región contigua más grande que se puede encontrar mediante la llamada al sistema subyacente dentro de la región disponible para el montón (que puede estar fragmentada por otras asignaciones)

Notas

Con respecto a las implementaciones de glibc y Linux, los siguientes fragmentos de manual son de gran interés:

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. 

Epílogo

Esta prueba se realizó en un kernel x86. Esperaría resultados similares de un núcleo x86_64, aunque con regiones de memoria mucho más grandes devueltas. Otros sistemas operativos pueden variar en la colocación de las asignaciones y el manejo de grandes malloc , por lo que los resultados podrían ser bastante diferentes.

¡Eso depende de tu implementación malloc!

Según Wikipedia, “desde la versión v2.3, la biblioteca C de GNU (glibc) usa un ptmalloc2 modificado, que a su vez se basa en dlmalloc v2.7.0”. dlmalloc se refiere a la implementación malloc de Doug Lea. Lo importante a tener en cuenta en esta implementación es que los mallocs grandes se logran a través de la funcionalidad del archivo mapeado de memoria del sistema operativo, por lo que estos bloques pueden ser bastante grandes sin muchos problemas para encontrar un bloque contiguo.

La pregunta malloc es respondida (depende del sistema operativo, que no se especifica), por lo que sobre eso se define:

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

no es seguro, alguien podría hacer DN(a+b,c) que se expande a

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

que probablemente no es lo que querías. Así que pon un montón de paréntesis allí:

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

para ver a qué DN(indx,jndx) , simplemente printf("%d\n",DN(indx,jndx));

El parámetro de tamaño en una llamada a malloc es del tipo size_t, que varía según la implementación. Vea esta pregunta para más.

Esto me hizo pensar ¿cuál es el mayor valor aquí?

26’901 ^ 2 = 723’663’801. Si su doble tiene 8 bytes, entonces es menos de 8GB. No veo ningún problema para asignar tanta memoria y mis aplicaciones asignan rutinariamente (en sistemas de 64 bits) mucho más. (El consumo de memoria más grande que he visto fue de 420 GB (en el sistema Numa de Solaris 10 con 640 GB de RAM) con el bloque continuo más grande de ~ 24 GB).

El valor más grande es difícil de identificar ya que depende de la plataforma: de manera similar a los sistemas de 32 bits, depende de la división espacio-usuario / espacio-kernel. Tal como están las cosas en este momento, creo que primero se llegaría al límite de la RAM física real, antes de llegar al límite de lo que la libc puede asignar. (Y a Kernel no le importa, simplemente expande la memoria virtual a menudo sin siquiera considerar si hay suficiente RAM para fijarla).

El bloque de memoria más grande que puede pedirle a malloc() es el valor más grande de size_t , este es SIZE_MAX de . La cantidad más grande que puede solicitar con éxito depende obviamente del sistema operativo y la configuración de la máquina individual.

Tu macro no es segura. Realiza el cálculo del índice con una variable int , que solo requiere un rango de hasta 32767. Cualquier valor superior a este puede causar un desbordamiento firmado, lo que da como resultado un comportamiento indefinido. Probablemente sea mejor que haga el cálculo como size_t , ya que ese tipo debe poder contener cualquier índice de matriz válido:

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

(Aunque tenga en cuenta que si proporciona valores negativos para i o j , obtendrá un índice muy fuera de límites).