domingo, noviembre 09, 2008

memcached: Arquitectura interna

(vía reddit.programming). Hace algún tiempo leía un artículo sobre el mecanismo de caché que utiliza Google App Engine está basado el proyecto de fuentes abiertas mencached: un sistema distribuido de gestión de caches genérico, pero usado sobre todo en aplicaciones web. Entre sus usuarios se puede destacar a Facebook o LiveJournal. Para conseguir velocidad utiliza una librería llamada libevent, que permite reemplazar los clásicos bucles de eventos que se encuentran en los programas servidores con un mecanismo basado un funciones callbacks y que permite escalar hasta un número ilimitado de descriptores de ficheros, usa funciones de entrada y salida no bloqueantes y por último, su propio sistema de gestión de memoria, basada en un slab allocator. A peek at memcached's implementation, da una descripción del funcionamiento de la gestión de memoria de esta librería. Este sistema es similar al gestor de memoria del núcleo de Linux e a su vez deriva del implementado en Solaris: Slab allocator (ver el artículo de Jeff Bonwick The slab allocator: An object-caching kernel memory allocator).Se puede leer un poco más de este tipo de gestión en Anatomy of the Linux slab allocator, en especial las referencias que aporta. Por otra parte se intenta garantizar que toda la gestión de memoria se realice con algoritmos en tiempo O(1) y que no exista fragmentación.

En las fuentes de memcached, en el fichero doc/memory_management.txt viene explicado todo el mecanismo de gestión de memoria. Si miramos el fichero slab.c puede verse toda la implementación del slab allocator.

La idea tras este tipo de gestión de memoria es simple: pedir al sistema operativo memoria en unos determinados tamaños,64 bytes, 128 bytes, 256 bytes, hasta un máximo - en el caso de memcached 1 MB -,creando listas con objetos de cada uno de los tamaños (ver slabs.c/slabs_init() ). Cuando el sistema necesita almacenar un objeto, se utiliza aquel trozo de memoria que más se ajuste al tamaño del mismo (en vez de llamar al sistema operativo usando malloc()). En caso de necesitar más memoria memcached lo que hace es reservar un trozo de 1 MB, dividirlo en los trozos necesarios (siempre con los tamaños predefinidos) y asignarlos a la listas libres.

La función slabs.c/slabs_alloc() es la encargada de reservar memoria en uno de los tamaños predeterminados (el slab determinado para un tamaño de objeto se obtiene con slabs.c/slabs_clsid(). Para liberar memoria se usa slabs.c/do_slabs_free().

Con este sistema se evita la fragmentación si se estuviese usando una gestión de memoria basada en malloc() y free(). En contra partida, se desperdicia memoria, porque hay que usar un bloque de memoria con el tamaño adecuado. Así si estamos usando una arquitectura de slabs con una secuencia de 64 bytes, 128 bytes, 256 bytes, 512 bytes, ..., un objeto de 70 bytes tendría que usar un slab de 128 bytes.

1 comentario:

Anónimo dijo...

Muy bueno gracias.