¿Cuello de botella de Asignación/Desasignación de Memoria?


¿Cuánto de un cuello de botella es la asignación/desasignación de memoria en programas típicos del mundo real? Las respuestas de cualquier tipo de programa donde el rendimiento generalmente importa son bienvenidas. ¿Son las implementaciones decentes de la colección malloc/free/garbage lo suficientemente rápidas como para que solo sea un cuello de botella en unos pocos casos de esquina, o la mayoría del software crítico de rendimiento se beneficiaría significativamente de intentar mantener la cantidad de asignaciones de memoria baja o tener una colección malloc/free/garbage más rápida ¿implementación?

Nota: Estoy no hablando de cosas en tiempo real aquí. Por rendimiento crítico, me refiero a cosas donde el rendimiento importa, pero la latencia no necesariamente.

Editar: Aunque menciono malloc, esta pregunta no pretende ser específica de C/C++.

Author: EmeryBerger, 2009-01-22

12 answers

Es significativo, especialmente a medida que crece la fragmentación y el asignador tiene que buscar más a través de montones más grandes para las regiones contiguas que solicite. La mayoría de las aplicaciones sensibles al rendimiento normalmente escriben sus propios asignadores de bloques de tamaño fijo (por ejemplo, piden al sistema operativo 16MB de memoria a la vez y luego la distribuyen en bloques fijos de 4kb, 16kb, etc.) para evitar este problema.

En juegos he visto llamadas a malloc () / free () consumir tanto como 15% de la CPU( en productos mal escritos), o con asignadores de bloques cuidadosamente escritos y optimizados, tan solo 5%. Dado que un juego tiene que tener un rendimiento consistente de sesenta hertz, que se detenga durante 500 ms mientras que un recolector de basura se ejecuta ocasionalmente no es práctico.

 35
Author: Crashworks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-22 20:41:25

Casi todas las aplicaciones de alto rendimiento ahora tienen que usar subprocesos para explotar la computación paralela. Aquí es donde el asesino de la velocidad de asignación de memoria real entra en juego al escribir aplicaciones C/C++.

En una aplicación C o C++, malloc/new debe tener un bloqueo en el montón global para cada operación. Incluso sin bloqueos de contención están lejos de ser libres y deben evitarse tanto como sea posible.

Java y C# son mejores en esto porque threading fue diseñado desde el principio y el los asignadores de memoria funcionan desde grupos de subprocesos. Esto también se puede hacer en C/C++, pero no es automático.

 20
Author: Zan Lynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-22 20:49:11

En primer lugar, ya que dijiste malloc, asumo que estás hablando de C o C++.

La asignación y desasignación de memoria tienden a ser un cuello de botella significativo para los programas del mundo real. Mucho pasa "bajo el capó" cuando se asigna o desasignar memoria, y todo es específico del sistema; la memoria puede ser realmente movida o desfragmentada, las páginas pueden ser reorganizadas no no hay una forma independiente de la plataforma de saber cuál será el impacto. Algunos sistemas (como muchas consolas de juegos) tampoco lo hacen desfragmentación de memoria, por lo que en esos sistemas, comenzará a obtener errores de memoria a medida que la memoria se fragmenta.

Una solución típica es asignar tanta memoria por adelantado como sea posible, y aferrarse a ella hasta que el programa salga. Puede usar esa memoria para almacenar grandes conjuntos monolíticos de datos o usar una implementación de grupo de memoria para repartirlos en trozos. Muchas implementaciones de bibliotecas estándar de C / C++ hacen una cierta cantidad de acumulación de memoria por sí mismas solo para esto motivo.

Sin embargo, no hay dos maneras de hacerlo if si tiene un programa C/C++ sensible al tiempo, hacer una gran cantidad de asignación/desasignación de memoria matará el rendimiento.

 11
Author: MattK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-22 20:47:19

En general, el costo de la asignación de memoria es probablemente empequeñecido por la contención de bloqueo, la complejidad algorítmica u otros problemas de rendimiento en la mayoría de las aplicaciones. En general, diría que esto probablemente no está en el top-10 de problemas de rendimiento que me preocuparía.

Ahora, agarrar trozos muy grandes de memoria podría ser un problema. Y agarrar pero no deshacerme adecuadamente de la memoria es algo de lo que me preocuparía.

En lenguajes basados en Java y JVM, new'ing objects ahora es muy, muy, muy rápido.

Aquí hay un artículo decente de un tipo que conoce sus cosas con algunas referencias en la parte inferior a enlaces más relacionados: http://www.ibm.com/developerworks/java/library/j-jtp09275.html

 7
Author: Alex Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-22 20:42:05

En Java (y potencialmente en otros lenguajes con una implementación de GC decente) asignar un objeto es muy barato. En la JVM SUN solo necesita 10 ciclos de CPU. Un malloc en C / c++ es mucho más caro, solo porque tiene que hacer más trabajo.

Aún así, incluso la asignación de objetos en Java es muy barata, hacerlo para muchos usuarios de una aplicación web en paralelo todavía puede conducir a problemas de rendimiento, porque se activarán más ejecuciones de Recolectores de basura. Por lo tanto, hay aquellos indirectos costes de una asignación en Java causada por la desasignación realizada por el GC. Estos costos son difíciles de cuantificar porque dependen mucho de su configuración (cuánta memoria tiene) y su aplicación.

 5
Author: kohlerm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-25 18:38:58

Una máquina virtual Java reclamará y liberará memoria del sistema operativo prácticamente independientemente de lo que está haciendo el código de la aplicación. Esto le permite agarrar y liberar memoria en trozos grandes, lo que es enormemente más eficiente que hacerlo en pequeñas operaciones individuales, como se obtiene con la administración manual de memoria.

Este artículo fue escrito en 2005, y la gestión de memoria al estilo JVM ya estaba por delante. La situación no ha mejorado desde entonces.

Que el lenguaje presume de raw más rápido rendimiento de asignación, el Java lenguaje, o C / C++? La respuesta puede te sorprenderá allocation asignación en moderno JVMs es mucho más rápido que el mejor realización de implementaciones malloc. El ruta de código común para new Object () en HotSpot 1.4.2 y posterior es aproximadamente 10 instrucciones de la máquina (datos proporcionados por Sun; ver Recursos), mientras que el mejor rendimiento malloc las implementaciones en C requieren promedio entre 60 y 100 instrucciones por llamada (Detlefs, et. al.; ver Recursos). Y asignación el rendimiento no es un componente trivial del rendimiento general-parámetros de referencia mostrar que muchos C y C++del mundo real programas, tales como Perl y Ghostscript, gastar 20 a 30 por ciento de su tiempo total de ejecución en malloc y gratis far mucho más que el asignación y recolección de basura sobrecarga de un Java saludable aplicación.

 4
Author: skaffman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-06 09:23:58

Asignar y liberar memoria en términos de rendimiento son operaciones relativamente costosas. Las llamadas en los sistemas operativos modernos tienen que ir hasta el núcleo para que el sistema operativo sea capaz de lidiar con la memoria virtual, paginación/mapeo, protección de ejecución, etc.

Por otro lado, casi todos los lenguajes de programación modernos esconden estas operaciones detrás de "asignadores" que funcionan con búferes preasignados.

Este concepto también es utilizado por la mayoría de las aplicaciones que se centran en el rendimiento.

 3
Author: Kosi2801,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-22 20:44:16

Sé que respondí antes, sin embargo, eso fue una respuesta a la otra respuesta, no a su pregunta.

Para hablar con usted directamente, si entiendo correctamente, su criterio de caso de uso de rendimiento es rendimiento.

Esto para mí, significa que deberías estar mirando casi exclusivamente a NUMA consciente asignadores .

Ninguna de las referencias anteriores; IBM JVM paper, Microquill C, SUN JVM. Cubrir este punto por lo que soy muy sospechoso de su aplicación hoy en día, donde, al menos en el AMD ABI, NUMA es el gobernador preeminente de la memoria-cpu.

Sin duda; mundo real, mundo falso, cualquier mundo... Las tecnologías de solicitud/uso de memoria NUMA son más rápidas. Desafortunadamente, estoy ejecutando Windows actualmente, y no he encontrado el "numastat" que está disponible en Linux.

Un amigo mío ha escrito sobre esto en profundidad en su implementación para el núcleo de FreeBSD.

Dispite me ser capaz de mostrar at-hoc, la cantidad típicamente muy grande de solicitudes de memoria de nodo local en la parte superior del nodo remoto (subrayando el rendimiento obvio rendimiento ventaja), usted puede hosco benchmark a ti mismo, y que probablemente sería lo que necesita todo como su charicterisitc rendimiento va a ser muy específico.

Lo sé de muchas maneras, al menos antes 5.x VMWARE carecía bastante mal, al menos en ese momento, por no aprovechar NUMA, páginas con frecuencia exigentes desde el nodo remoto. Sin embargo, las máquinas virtuales son una bestia muy única cuando se trata de compartimentación de memoria o contenerización.

Una de las referencias que cité es a la implmentación de la API de Microsoft para AMD ABI, que tiene interfaces especializadas de asignación NUMA para que los desarrolladores de aplicaciones de tierra de usuario exploten;)

Aquí hay un análisis bastante reciente , visual y todo, de algunos desarrolladores de complementos del navegador que comparan 4 implementaciones de montón diferentes. Naturalmente, el que desarrollado resulta en la parte superior (extraño cómo las personas que hacen las pruebas a menudo exhiben la puntuación más alta).

Cubren de alguna manera cuantificable, al menos para su caso de uso, cuál es la compensación exacta entre espacio/tiempo, generalmente habían identificado el LFH (oh ya y por cierto LFH es simplemente un modo aparentemente del montón estándar) o enfoque diseñado de manera similar esencialmente consume significativamente más memoria del murciélago sin embargo, con el tiempo, puede terminar usando menos memoria... el grafix también está limpio...

Sin embargo, creo que seleccionar una implmentación de MONTÓN basada en su carga de trabajo típica después de entenderla bien;) es una buena idea, pero para comprender bien sus necesidades, primero asegúrese de que sus operaciones básicas sean correctas antes de optimizar estas probabilidades y extremos;)

 3
Author: RandomNickName42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-06 09:01:27

Aquí es donde el sistema de asignación de memoria de c/c++funciona mejor. La estrategia de asignación predeterminada está bien para la mayoría de los casos, pero se puede cambiar para adaptarse a lo que sea necesario. En los sistemas GC no hay mucho que pueda hacer para cambiar las estrategias de asignación. Por supuesto, hay un precio a pagar, y esa es la necesidad de rastrear las asignaciones y liberarlas correctamente. C++ lleva esto más allá y la estrategia de asignación se puede especificar por clase usando el nuevo operador:

class AClass
{
public:
  void *operator new (size_t size); // this will be called whenever there's a new AClass
   void *operator new [] (size_t size); // this will be called whenever there's a new AClass []
  void operator delete (void *memory); // if you define new, you really need to define delete as well
  void operator delete [] (void *memory);define delete as well
};

Muchas de las plantillas STL le permiten definir asignadores personalizados también.

Al igual que con todas las cosas que tienen que ver con la optimización, primero debe determinar, a través del análisis de tiempo de ejecución, si la asignación de memoria realmente es el cuello de botella antes de escribir sus propios asignadores.

 3
Author: Skizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-03 14:31:25

De acuerdo con MicroQuill SmartHeap Especificación Técnica, "una aplicación típica [...] gasta el 40% de su tiempo total de ejecución en la gestión de la memoria". Puede tomar esta cifra como un límite superior, personalmente siento que una aplicación típica gasta más como 10-15% del tiempo de ejecución asignando/desasignando memoria. Rara vez es un cuello de botella en la aplicación de un solo hilo.

En aplicaciones C/C++ multiproceso los asignadores estándar se convierten en un problema debido a la contención de bloqueo. Este es donde comienza a buscar soluciones más escalables. Pero ten en mente la Ley de Amdahl.

 2
Author: Constantin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-02 09:08:46

Otros han cubierto C / C++, así que solo agregaré un poco de información en. NET.

En.NET la asignación de montones es generalmente muy rápida, ya que solo es cuestión de agarrar la memoria en la parte de generación cero del montón. Obviamente esto no puede continuar para siempre, que es donde entra la recolección de basura. La recolección de basura puede afectar significativamente el rendimiento de su aplicación, ya que los hilos de usuario deben suspenderse durante la compactación de la memoria. Cuanto menos recolecciones completas, mejor.

Hay varias cosas que puede hacer para afectar la carga de trabajo del recolector de basura en .NET. Generalmente, si tiene mucha referencia de memoria, el recolector de basura tendrá que hacer más trabajo. Por ejemplo, al implementar un gráfico utilizando una matriz de adyacencia en lugar de referencias entre nodos, el recolector de basura tendrá que analizar menos referencias.

Si eso es realmente significativo en su aplicación o no depende de varios factores y debe perfilar la aplicación con datos reales antes de recurrir a tales optimizaciones.

 1
Author: Brian Rasmussen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-02 09:31:12

Casi todos ustedes son off base si usted está hablando de la pila de Microsoft. La sincronización se maneja sin esfuerzo al igual que la fragmentación.

El montón perferrred actual es el LFH, (BAJA FRAGMENTATION HEAP), es predeterminado en vista+ OS y se puede configurar en XP, a través de gflag, sin muchos problemas

Es fácil evitar cualquier problema de bloqueo/bloqueo/contención / bus-bandwitth y el lote con el

HEAP_NO_SERIALIZE

Opción durante HeapAlloc o HeapCreate. Esto le permitirá crear / usar un montón sin entrar en una espera entrelazada.

Recomendaría crear varios montones, con HeapCreate, y definir una macro, tal vez, mallocx (enum my_heaps_set, size_t);

Estaría bien, por supuesto, necesita realloc, libre también para ser configurado como apropiado. Si desea obtener fantasía, haga que free / realloc detecte automáticamente qué manejador de montón por su cuenta evaluando la dirección del puntero, o incluso agregando algo de lógica a permite a malloc identificar qué montón usar en función de su id de hilo, y construir una heierarquía de montones por hilo y montones/pools globales compartidos.

Las api de Heap* son llamadas internamente por malloc/new.

Aquí hay un buen artículo sobre algunos problemas dinámicos de gestión de memoria, con algunas referencias aún más agradables . Para instrumentar y analizar la actividad del montón.

 1
Author: RandomNickName42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-02 11:27:36