



(2 opiniones)
Utilizar múltiples montones puede incrementar el rendimiento, sobre todo poniendo énfasis en los siguientes puntos:
Un montón por cada hilo
En situaciones de acceso masivo al montón, se puede producir un cuello de botella cuando múltiples hilos acceden al montón por defecto repetidas veces (miles o millones). En estas situaciones es recomendable crear un montón por cada hilo, y realizar todas las peticiones de memoria al montón privado de cada hilo. Además, en este caso se puede (y se debe) desactivar el mecanismo de sincronización de hilos (utilizando la bandera HEAP_NO_SERIALIZE), ya que será un solo hilo el que haga accesos a cada montón, y este mecanismo ralentiza la ejecución.
Suponiendo que estamos programando un programa servidor, podríamos crear un hilo que gestione las peticiones de cada cliente que se conecta a nuestro servidor. Además, si estos hilos hacen un uso intensivo de la memoria dinámica, es muy recomendable crear un montón para cada uno de ellos, utilizando la bandera HEAP_NO_SERIALIZE en la llamada a HeapCreate.
Un montón para cada tipo de dato
Si utilizamos el montón por defecto para almacenar estructuras de datos, lo más probable, como ya hemos explicado, es que después de las primeras asignaciones/liberaciones, tengamos un montón con bloques de memoria fragmentados. Para aclarar esto, nada mejor que un ejemplo: supongamos que tenemos un montón 140 KB y hacemos las siguientes reservas:
Reservar 20 KB
Reservar 10 KB
Reservar 50 KB
Reservar 40 KB


Esta situación, se podría evitar si utilizamos un montón por cada tipo de dato a almacenar.
Supongamos que necesitamos almacenar dos listas enlazadas: la primera de elementos de 5 KB y la segunda de elementos de 7 KB. Si ambas listas se almacenan en el mismo montón, podríamos llegar fácilmente a situaciones como la descrita. Sin embargo, si utilizamos un montón para cada lista, los bloques siempre serán lo suficientemente grandes, porque los "huecos" serán siempre de un tamaño múltiplo del espacio requerido (5 y 7 KB respectivamente) y los nuevos bloques siempre "encajarán" en estos "huecos". En esta figura
se muestra un montón fragmentado, pero que acomodaría perfectamente cualquier petición de 7 KB (representa el montón adicional para la segunda lista enlazada).
Situar los bloques de memoria próximos
Es conveniente que los bloques de memoria que vayan a ser utilizamos a la vez se reserven dentro de un rango de direcciones virtuales lo más pequeño posible. Esto es debido a que, cuando el sistema necesita más memoria, vuelca cierto rango de páginas al archivo de intercambio para dejar espacio libre en RAM. Si los bloques de memoria que necesitamos no están próximos entre sí, puede darse el caso de que nuestros datos hayan sido volcados al archivo de intercambio, con lo cual sería necesario volverlos a recuperar de disco y proyectarlos en memoria, lo cual es una operación muy lenta. Utilizando un montón para cada estructura de datos, conseguimos que los bloques de memoria que se van a utilizar a la vez se direccionen juntos, con lo que minimizamos el riesgo de que algunos de ellos sean volcados al archivo de intercambio.
En el ejemplo que pusimos anteriormente, es recomendable que los bloques de ambas listas enlazadas, se sitúen próximos entre sí dentro del sistema de memoria virtual, y esto se consigue utilizando un montón para cada una de ellas
Proteger componentes
Si en el mismo montón, mezclamos bloques de memoria de distintas estructuras, corremos el riesgo de que una escritura errónea en la manipulación de una de ellas, pueda afectar a los datos de la otra. En nuestro ejemplo, si cometemos un error al manipular la primera lista enlazada, podemos sobrescribir datos de la segunda lista, lo cual sería difícil de depurar, máxime si ambas estructuras se utilizan desde partes muy distintas del programa. Es mucho más conveniente aislar cada una de las estructuras en su propio montón, para evitar así que un error en una parte de un programa, afecte a sus datos, y no a los datos de otros objetos.
Este método es especialmente recomendable para proteger componentes encapsulados dentro de una DLL, ya que así nos aseguramos que no corromperemos la memoria del programa, sino la de nuestra propia DLL.
|