31 enero 2008

Consumo de memoria de las imágenes

Las imágenes en .net se guardan como Bitmap, única herencia de Image (la otra es Metafile). Como nosotros tenemos propiedades de imágenes en las entidades, mientras una entidad está cargada se mantienen en memoria sus imágenes. No se cargan con la entidad, sólo cuando se necesitan, pero una vez cargadas ya se mantienen con la entidad. Al hojear el Catálogo comercial de N2 Frax, se carga una colección con los 130 artículos del catálogo, y si vamos pasando las páginas una a una al llegar a la 30 tenemos en uso 500 Mb de memoria RAM, y Windows se vuelve loco usando memoria virtual, el sistema prácticamente se congela.
Las imágenes de este catálogo son mucho más grandes que temporadas anteriores (se han sacado con máxima calidad), lo que ha agravado el problema. Aunque sean archivos png de unos 70 Kb, en memoria se guardan como Bitmap, por lo que el uso de memoria es tremendo.
He encontrado pocas historias parecidas en Internet. Un tío dice que le falla en ASP, pero nadie le contesta. Hay pocas referencias a la cuestión de cachear imágenes en memoria. Un colega cuenta su experiencia desarrollando una caché de imágenes que libera imágenes cuando supera un número de Kb en memoria.
Así que vemos la necesidad de limitar el número de imágenes almacenadas en memoria, para lo que deben almacenarse en un lugar centralizado: CentroImagenes, en vez de variables privadas en las entidades. Así puede controlarse el volumen de memoria utilizado, y liberar cuando se alcance una cantidad que podrá parametrizarse.
Surge un problema: cuando asignamos una imagen a una entidad, no debe guardarse en disco hasta guardar la entidad. Así que creamos nuevos métodos en CentroImagenes:
  • Preasignar(clave, imagen) para incluir la imagen en caché pero no en disco. Debe llamarse en el set de la propiedad imagen de la entidad.
  • Confirmar(clave) para guardar en disco una imagen que ya hemos preasignado en caché. Debe llamarse al guardar la entidad.
  • Descartar(clave) para quitar de caché una imagen que hemos preasignado antes. Debe llamarse en el deshacer de la entidad.

Con esto nunca se almacena una imagen en negocio, sino que todas se centralizan en CentroImagenes, y así podemos liberar a voluntad... aunque hay que intentar no liberar una imagen preasignada (si no cuando se intente guardar su entidad se habrá perdido la imagen que asignó el usuario).

Para liberar, vamos a consultar el consumo en memoria, pero no encuentro cómo consultar cuanta memoria ocupa la caché, o cuanta ocupa cada imagen, así que habrá que estimarla: al incluir cada imagen, calcularemos su número de pixeles y supondremos que cada uno ocupa 4 bytes (lo he leído en algún sitio). Habrá que descontar también cuando quitemos imágenes de la caché.

Etiquetas: ,

4 Comments:

Blogger Bernardo said...

Se me ocurre una duda, en el caso (reconozco que poco probbable) de que las imágenes aún no guardadas lleguen a ocupar todo el espacio reservado en el CentroImagenes. ¿Cómo descargar memoria sin "definitivizar" las imágenes para los objetos? ¿guardándolas en un repositorio temporal en el disco?, se podría contemplar el caso sobre todos si se va a permitir configurar el tamaño de la caché de imagenes.

01 febrero, 2008 10:50  
Blogger Pablo said...

Pues llevas razón en que hay un problema. Pero no me gusta la solución: un repositorio temporal sería el disco, y eso es lo que hace Windows con la memoria virtual, así que ¿para qué copiar en disco para evitar que Windows lo haga? Por supuesto, no se pueden descargar imágenes preasignadas (sólo están en memoria), ni forzar a persistir (el programador aún no ha guardado la entidad), así que la única solución es dejar que la memoria en este caso crezca descontroladamente. El programador verá que hace mal al preasignar tantas imágenes, y deberá guardar más a menudo.

01 febrero, 2008 11:56  
Blogger Pablo said...

Han surgido cuestiones en la revisión con Berny y Carvy.
Sobre el tamaño límite del centro de imágenes, probando el catálogo vemos que se pone lento (se pagina) a partir de 300 Mb en un equipo con 512 Mb, así que mi idea inicial de 80% de la memoria física y la de Carvy (10%) se quedan en un bonito punto medio del 50%. Este será el valor establecido automáticamente, aunque podrá cambiarse a través de una propiedad pública.
Otra cuestión es la liberación de imágenes: deberá hacerse al descargar las entidades (necesitan IDisposable), y sobre todo cuando las imagenes se preasignan y luego no se guardar (si no, no se liberarían nunca). Para que esta liberación se pueda hacer en EBase, necesitamos detectar automáticamente las propiedades de imagen de una entidad, y seguir una regla: si la propiedad se llama Imagen, guardar como EEntidad.1.img; si no, guardar como EEntidad.Prop.1.img; igual con propiedad Icono.
Y una idea a profundizar: ¿podríamos guardar las imágenes en memoria como PNG? Para esto, podríamos escribirlas en un MemoryStream, y recuperarlas de ahí antes de devolverlas. Incluso se podría implementar una clase ImagePng que herede de Image y que realice esto, con el considerable ahorro en consumo de memoria que supondría.

01 febrero, 2008 12:16  
Blogger Jorge said...

Me gusta la idea de la clase ImagePng , esto supondria el no tener que usar un centro de imagenes no??
Puede que sea un curro lo de imagePng pero parece mas intuitivo de usar en mi opinion.
Tener imagenes en memoria que no se guarden como bitmap supondria no tener que gestionar todo ese excesivo uso de memoria, se necesitarian unas 1500 imagenes de unos 70kb para alcamzar un cuota de 100mb en memoria.

15 febrero, 2008 12:23  

Publicar un comentario

<< Home