Anuncios

Bienvenidos sean a este post, el nucleo de Lua 5.1 no asume nada acerca de como asignar memoria, no llama ni a malloc o realloc para asignar memoria, en su lugar hace la asignacion o desasignacion de memoria a traves de una unica funcion de asignacion, la cual el usuario debe proveer cuando se crea un estado de Lua.

Anuncios

La funcion luaL_newstate, la cual hemos estado usando para crear estados, es una funcion auxiliar que crea un estado de Lua con una funcion de asignacion predeterminada, dicha funcion usa las funciones estandar malloc-realloc-free de la libreria estandar de C, las cuales son (o deberian ser) suficiente para regular aplicaciones, sin embargo es facil conseguir control sobre la asignacion en Lua creando todo estado con el primitivo lua_newstate:

lua_State *lua_newstate (lua_alloc f, void *ud);
Anuncios

Esta funcion recibe dos argumentos: una funcion de asignacion y un userdata, un estado creado de esta manera hace todas las asignaciones de memoria y desasignacion por la llamada de f, incluso la estructura de lua_State es asignada por f, el tipo lua_Alloc de la funcion de asignacion es definido como sigue:

typedef void * (*lua_Alloc) (void *ud,
			     void *ptr,
			     size_t osize,
			     size_t nsize);
Anuncios

El primer parametro es siempre el userdata que proveemos para lua_newstate, el segundo es la direccion del bloque siendo reasignado o desasignado, el tercero es el tamaño del bloque original y el ultimo es el tamaño del bloque solicitado, Lua asegura que:

Si ptr no es NULL significa que osize fue previamente asignado, Lua identifica NULL con bloques de tamaño cero, en cambio si ptr es NULL, entonces (y solo entonces) osize es cero

Anuncios
Anuncios

Lua no nos asegura que osize es diferente a nsize, incluso cuando ambos son cero; en estos casos la funcion de asignacion podria simplemente devolver ptr, la cual sera NULL cuando ambos son cero.

Lua espera que la funcion de asignacion tambien identifique NULL con bloques de tamaño cero, cuando nsize es cero la funcion de asignacion debe liberar el bloque señalado por ptr y devolver NULL, lo cual corresponde a un bloque del tamaño requerido (cero), cuando osize es cero, y por lo tanto ptr es NULL, la funcion debe asignar y devolver un bloque con el tamaño informado, si no puede asignar el bloque informado debe devolver NULL, si tanto osize como nsize son cero ambos descripciones previas aplican; el resultado neto es que la funcion de asignacion no hace nada y devuelve NULL, por ultimo cuando osize y nsize no son cero la funcion de asignacion deberia reasignar el bloque, como realloc, y devolver la nueva direccion (la cual podria o no podria ser la misma que la original), en caso de error debe devolver NULL, Lua asume que la funcion de asignacion nunca falla cuando el nuevo tamaño es menor o igual que al anterior, Lua encoge algunas estructuras durante la recoleccion de basura y esto nos vuelve incapaz de recuperarnos de errores ahi, la funcion de asignacion estandar usada por luaL_newstate tiene la siguiente definicion:

void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize)
{
	if (nsize == 0)
	{
		free(ptr);
		return NULL;
	} else {
		return realloc(ptr, nsize);
	}
}
Nota: esta funcion es extraida directamente de lauxlib.c.
Anuncios

Asumamos que la siguiente funcion:

free(NULL) 

No hace nada y que la siguiente funcion:

realloc(NULL, tamano) 

Es equivalente a la siguiente funcion:

malloc(tamano) 
Anuncios

Esta conducta es asegurada por el estandar ANSI C, se puede conseguir el asignador de memoria del estado de Lua llamando a lua_getallocf:

lua_Alloc lua_getallocf (lua_State *L, void **ud);

Si ud no es NULL la funcion setea a *ud con el valor del userdata para este asignador, se puede cambiar el asignador de memoria de un estado de Lua con la llamada a lua_setallocf:

void lua_setallocf(lua_State *L, lua_Alloc f, void *ud);
Anuncios

Ten en cuenta que cada nuevo asignador debera ser responsable por la liberacion de bloques que fueron asignados previamente, mas a menudo que no la nueva funcion es una envoltura alrededor de la antigua, por ejemplo para rastrear asignaciones o sincronizar accesos al heap.

Nota: queres saber sobre el heap en la pila, te recomiendo este post.
Anuncios
Anuncios

Internamente Lua no “cachea” bloques de memoria libre para su resuso, asume que la funcion de asignacion hace esto, los buenos asignadores lo hacen, tampoco intenta minimizar la fragmentacion ya que hay estudios que muestran a la fragmentacion como resultado de estrategias de asignacion pobres y no como una conducta del programa, asi que es dificil vencer a un asignador bien implementado pero algunas veces podes intentar esto:

Por ejemplo Lua te da el tamaño de cualquier bloque viejo que libera o reasigna, este dato no lo obtenes de la funcion free convencional por lo tanto un asignador especializado no necesita mantener informacion sobre el tamaño del bloque y como consecuencia reduciendo la sobrecarga de memoria por cada bloque.

Otra situacion donde se puede mejora la asignacion de memoria es en un sistema de multiprocesos, lo mas habitual es que estos sistemas demandan sincronizacion para sus funciones de asignacion de memoria, como ellos usan recursos globales (memoria) el acceso a estados de Lua debe ser sincronizado tambien, o mejor aun restringidos a un solo proceso, como en nuestra implementacion de lproc en este post, asi que cada estado de Lua asigna memoria de una pool privada y el asignador puede evitar los costos de una sincronizacion extra.

Anuncios

En resumen, hoy hemos visto como trabaja un asignador de memoria, como se hace con los metodos convencionales, como lo hace Lua, cuales son los metodos que podemos usar y como hace realmente la magia, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Tengo un Patreon donde podes acceder de manera exclusiva a material para este blog antes de ser publicado, sigue los pasos del link para saber como.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00