Malloc frente a asignador personalizado: Malloc tiene muchos gastos generales. ¿Por qué?

6 minutos de lectura

avatar de usuario
TravisG

Tengo una aplicación de compresión de imágenes que ahora tiene dos versiones diferentes de sistemas de asignación de memoria. En el original, malloc se usa en todas partes, y en el segundo, implementé un asignador de grupo simple, que solo asigna una parte de la memoria y devuelve partes de esa memoria a las llamadas myalloc().

Hemos notado una gran sobrecarga de memoria cuando se usa malloc: en el punto máximo de su uso de memoria, el código malloc() requiere alrededor de 170 megabytes de memoria para una imagen de 1920x1080x16bpp, mientras que el asignador de grupos asigna solo 48 megabytes, de los cuales 47 son utilizados por el programa.

En términos de patrones de asignación de memoria, el programa asigna muchos bloques de 8 bytes (la mayoría), 32 bytes (muchos) y 1080 bytes (algunos) con la imagen de prueba. Aparte de estos, no hay asignaciones de memoria dinámica en el código.

El sistema operativo del sistema de prueba es Windows 7 (64 bits).

¿Cómo probamos el uso de la memoria?

Con el asignador personalizado, pudimos ver cuánta memoria se usa porque todas las llamadas malloc se transfieren al asignador. Con malloc(), en el modo de depuración simplemente revisamos el código y observamos el uso de la memoria en el administrador de tareas. En el modo de lanzamiento hicimos lo mismo, pero menos detallado porque el compilador optimiza muchas cosas para que no pudiéramos revisar el código pieza por pieza (la diferencia de memoria entre el lanzamiento y la depuración era de unos 20 MB, lo que atribuiría a optimización y falta de información de depuración en el modo de lanzamiento).

¿Podría malloc por sí solo ser la causa de una sobrecarga tan grande? Si es así, ¿qué causa exactamente esta sobrecarga dentro de malloc?

  • Normalmente, solo escribe una versión personalizada de algo genérico cuando lo que escribe agregará muchos beneficios en el rendimiento debido a que conoce los detalles de cómo planea usarlo. Sin embargo, no esperaría una sobrecarga de memoria por usar malloc. ¿Estás seguro de que estás midiendo correctamente el uso de la memoria? ¿Estás seguro de que estás liberando la memoria correctamente cuando usas malloc?

    – CashCow

    25 de octubre de 2012 a las 8:51

  • Todo en el código malloc se libera (lo probé con un perfilador de memoria), pero solo al final de la aplicación antes de que termine de ejecutarse, por lo que las mediciones ocurren antes de que se llame a cualquier función free() (en ambas versiones). El asignador personalizado acelera todo y nos ahorra unos 15 ms por imagen (ya que es solo una asignación grande en lugar de muchas pequeñas).

    – TravisG

    25 de octubre de 2012 a las 8:57


  • Un 200% de gastos generales para malloc parece excesivo, a menos que haya muchas más asignaciones de 8 bytes de las que cree.

    –Steve Jessop

    25 de octubre de 2012 a las 8:57

  • malloc bien puede “retener” algo de memoria después de haberla liberado para su uso inmediato. Si su implementación usa std::vector, puede “reservar” con anticipación, aunque cuando va a asignar cantidades tan grandes de memoria, es mejor no elegir un modelo que requiera un búfer contiguo.

    – CashCow

    25 oct 2012 a las 9:00

  • ¿Pueden describir cómo midieron el uso de la memoria?

    – Um Nyobe

    25 de octubre de 2012 a las 9:04

En Windows 7, tendrá siempre obtenga el asignador de almacenamiento dinámico de baja fragmentación, sin llamar explícitamente a HeapSetInformation() para solicitarlo. Ese asignador sacrifica espacio de memoria virtual para reducir la fragmentación. Su programa en realidad no está usando 170 megabytes, solo está viendo un montón de bloques libres esperando una asignación de un tamaño similar.

Este algoritmo es muy fácil de superar con un asignador personalizado que no hace nada para reducir la fragmentación. Lo que bien puede funcionar para usted, aunque no verá los efectos secundarios hasta que mantenga el programa funcionando por más tiempo que una sola sesión de depuración. Debe asegurarse de que sea estable durante días o semanas si ese es el patrón de uso esperado.

Lo mejor que puedes hacer es simplemente no preocuparte por eso, 170 MB son papas bastante pequeñas. Y tenga en cuenta que esto es virtual memoria, no cuesta nada.

  • “En Windows 7, siempre obtendrá el asignador de almacenamiento dinámico de baja fragmentación” Lo obtendrá, a menos que esté ejecutando un depurador. Según la metodología descrita (recorriendo paso a paso el código), parece que se usó el depurador al medir el consumo de memoria, por lo tanto, no se usó LFH. Mira mi respuesta.

    – Suma

    25 de octubre de 2012 a las 9:46

  • Ese artículo cita la historia antigua, el administrador de memoria se revisó significativamente en Vista. Pero sí, el relleno de bloque utilizado por la función de depuración del montón puede afectar la cantidad de máquinas virtuales que ve que se utilizan.

    -Hans Passant

    25 de octubre de 2012 a las 9:58

  • La memoria virtual cuesta algo, las páginas perdidas son dolorosas en el rendimiento.

    – Fingolfin

    25 de octubre de 2012 a las 9:58

  • No, no puedes perder una página accediendo a un bloque gratuito. Eso sería un error.

    -Hans Passant

    25/10/2012 a las 10:00

  • @Hans Tal vez me equivoqué entonces, ¿puedes explicar esto más a fondo?

    – Fingolfin

    25 de octubre de 2012 a las 10:01


avatar de usuario
Aki Suihkönen

En primer lugar, malloc alinea los punteros con límites de 16 bytes. Además, almacenan al menos un puntero (o longitud asignada) en las direcciones que preceden al valor devuelto. Luego, probablemente agreguen un valor mágico o un contador de liberación para indicar que la lista enlazada no está rota o que el bloque de memoria no se ha liberado dos veces (ASSERTS libres para liberaciones dobles).

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

int main(int ac, char**av)
{
  int *foo = malloc(4);
  int *bar = malloc(4);
  printf("%d\n", (int)bar - (int)foo);
}

Vuelta: 32

  • ¿Quieres decir que 32 bits ocupan 16 bytes?

    – TravisG

    25 de octubre de 2012 a las 9:11

  • @Aki dice muchas asignaciones de 32 bytes. ¿Por qué se te ocurren asignaciones de 32 bits?

    – Um Nyobe

    25 de octubre de 2012 a las 9:15

  • Esto se prueba en ubuntu de 64 bits. En los sistemas de 32 bits, lo más probable es que se necesiten 16 bytes.

    – Aki Suihkonen

    25 de octubre de 2012 a las 9:16

  • @UmNyobe: aparentemente mi error. De todos modos, malloc de 8 bytes toma 32 bytes y 32 bytes malloc ocupa al menos 48 bytes.

    – Aki Suihkonen

    25 de octubre de 2012 a las 9:20

  • Gracias. La alineación podría explicar gran parte de la pérdida de memoria. Aki, ¿tienes alguna fuente para esta información?

    – TravisG

    25 de octubre de 2012 a las 9:21


avatar de usuario
suma

Precaución: cuando ejecuta su programa en Visual Studio o con cualquier depurador adjunto, el comportamiento de malloc cambia mucho de forma predeterminada. No se utiliza el montón de fragmentación baja y una sobrecarga de memoria puede no ser representativa del uso real (ver también https://stackoverflow.com/a/3768820/16673). Debe usar la variable de entorno _NO_DEBUG_HEAP=1 para evitar que esto le afecte o para medir el uso de la memoria cuando no se ejecuta con un depurador.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad