engine: common: zone: use realloc for managing mem pools

rearrange data in memheader_s to avoid unnecessary paddings
This commit is contained in:
Alibek Omarov 2024-04-18 19:45:54 +03:00
parent 9ec1caed53
commit e81b5144b3
2 changed files with 73 additions and 65 deletions

View File

@ -28,11 +28,7 @@ typedef byte rgb_t[3]; // unsigned byte colorpack
typedef vec_t matrix3x4[3][4]; typedef vec_t matrix3x4[3][4];
typedef vec_t matrix4x4[4][4]; typedef vec_t matrix4x4[4][4];
#if XASH_64BIT
typedef uint32_t poolhandle_t; typedef uint32_t poolhandle_t;
#else
typedef void* poolhandle_t;
#endif
#undef true #undef true
#undef false #undef false

View File

@ -55,9 +55,9 @@ typedef struct memheader_s
{ {
struct memheader_s *next; // next and previous memheaders in chain belonging to pool struct memheader_s *next; // next and previous memheaders in chain belonging to pool
struct memheader_s *prev; struct memheader_s *prev;
struct mempool_s *pool; // pool this memheader belongs to
size_t size; // size of the memory after the header (excluding header and sentinel2)
const char *filename; // file name and line where Mem_Alloc was called const char *filename; // file name and line where Mem_Alloc was called
size_t size; // size of the memory after the header (excluding header and sentinel2)
poolhandle_t poolptr; // pool this memheader belongs to
int fileline; int fileline;
#if !XASH_64BIT #if !XASH_64BIT
uint32_t pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32 uint32_t pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32
@ -74,45 +74,31 @@ typedef struct mempool_s
size_t totalsize; // total memory allocated in this pool (inside memheaders) size_t totalsize; // total memory allocated in this pool (inside memheaders)
size_t realsize; // total memory allocated in this pool (actual malloc total) size_t realsize; // total memory allocated in this pool (actual malloc total)
size_t lastchecksize; // updated each time the pool is displayed by memlist size_t lastchecksize; // updated each time the pool is displayed by memlist
struct mempool_s *next; // linked into global mempool list
const char *filename; // file name and line where Mem_AllocPool was called const char *filename; // file name and line where Mem_AllocPool was called
int fileline; int fileline;
#if XASH_64BIT
poolhandle_t idx;
#endif
char name[64]; // name of the pool char name[64]; // name of the pool
uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1 uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1
} mempool_t; } mempool_t;
static mempool_t *poolchain = NULL; // critical stuff static mempool_t *poolchain = NULL; // critical stuff
static size_t poolcount = 0;
#if XASH_64BIT
// a1ba: due to mempool being passed with the model through reused 32-bit field // a1ba: due to mempool being passed with the model through reused 32-bit field
// which makes engine incompatible with 64-bit pointers I changed mempool type // which makes engine incompatible with 64-bit pointers I changed mempool type
// from pointer to 32-bit handle, thankfully mempool structure is private // from pointer to 32-bit handle, thankfully mempool structure is private
// But! Mempools are handled through linked list so we can't index them safely
static poolhandle_t lastidx = 0;
static mempool_t *Mem_FindPool( poolhandle_t poolptr ) static mempool_t *Mem_FindPool( poolhandle_t poolptr )
{ {
mempool_t *pool; if( likely( poolptr > 0 && poolptr <= poolcount ))
return &poolchain[poolptr - 1];
for( pool = poolchain; pool; pool = pool->next )
{
if( pool->idx == poolptr )
return pool;
}
Sys_Error( "%s: not allocated or double freed pool %d", __FUNCTION__, poolptr ); Sys_Error( "%s: not allocated or double freed pool %d", __FUNCTION__, poolptr );
return NULL; return NULL;
} }
#else
static mempool_t *Mem_FindPool( poolhandle_t poolptr ) static poolhandle_t Mem_PoolIndex( mempool_t *mempool )
{ {
return (mempool_t *)poolptr; return (poolhandle_t)(mempool - poolchain) + 1;
} }
#endif
static inline void Mem_PoolAdd( mempool_t *pool, size_t size ) static inline void Mem_PoolAdd( mempool_t *pool, size_t size )
{ {
@ -132,7 +118,7 @@ static inline void Mem_PoolLinkAlloc( mempool_t *pool, memheader_t *mem )
if( mem->next ) mem->next->prev = mem; if( mem->next ) mem->next->prev = mem;
pool->chain = mem; pool->chain = mem;
mem->prev = NULL; mem->prev = NULL;
mem->pool = pool; mem->poolptr = Mem_PoolIndex( pool );
} }
static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem ) static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
@ -140,7 +126,7 @@ static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
if( mem->next ) mem->next->prev = mem->prev; if( mem->next ) mem->next->prev = mem->prev;
if( mem->prev ) mem->prev->next = mem->next; if( mem->prev ) mem->prev->next = mem->next;
else pool->chain = mem->next; else pool->chain = mem->next;
mem->pool = NULL; mem->poolptr = 0;
} }
static inline void Mem_InitAlloc( memheader_t *mem, size_t size, const char *filename, int fileline ) static inline void Mem_InitAlloc( memheader_t *mem, size_t size, const char *filename, int fileline )
@ -243,7 +229,7 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline
if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline )) if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline ))
return; return;
pool = mem->pool; pool = Mem_FindPool( mem->poolptr );
// unlink memheader from doubly linked list // unlink memheader from doubly linked list
if(( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem )) if(( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem ))
@ -324,9 +310,11 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
// if allocation was migrated from one pool to another // if allocation was migrated from one pool to another
// (this is possible with original Mem_Realloc func) // (this is possible with original Mem_Realloc func)
if( unlikely( mem->pool != pool )) if( unlikely( mem->poolptr != poolptr ))
{ {
Mem_PoolUnlinkAlloc( mem->pool, mem ); mempool_t *oldpool = Mem_FindPool( mem->poolptr );
Mem_PoolUnlinkAlloc( oldpool, mem );
Mem_PoolLinkAlloc( pool, mem ); Mem_PoolLinkAlloc( pool, mem );
} }
else if( oldmem != (uintptr_t)mem ) // just relink pointers else if( oldmem != (uintptr_t)mem ) // just relink pointers
@ -339,56 +327,67 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
return (void *)((byte *)mem + sizeof( memheader_t )); return (void *)((byte *)mem + sizeof( memheader_t ));
} }
poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline ) static poolhandle_t Mem_InitPool( mempool_t *pool, const char *name, const char *filename, int fileline )
{ {
mempool_t *pool; memset( pool, 0, sizeof( *pool ));
pool = (mempool_t *)Q_malloc( sizeof( mempool_t ));
if( pool == NULL )
{
Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline );
return 0;
}
memset( pool, 0, sizeof( mempool_t ));
// fill header // fill header
pool->sentinel1 = MEMHEADER_SENTINEL1; pool->sentinel1 = MEMHEADER_SENTINEL1;
pool->sentinel2 = MEMHEADER_SENTINEL1; pool->sentinel2 = MEMHEADER_SENTINEL1;
pool->filename = filename; pool->filename = filename;
pool->fileline = fileline; pool->fileline = fileline;
pool->chain = NULL;
pool->totalsize = 0;
pool->realsize = sizeof( mempool_t ); pool->realsize = sizeof( mempool_t );
Q_strncpy( pool->name, name, sizeof( pool->name )); Q_strncpy( pool->name, name, sizeof( pool->name ));
pool->next = poolchain;
poolchain = pool;
#if XASH_64BIT return Mem_PoolIndex( pool );
pool->idx = ++lastidx; }
return pool->idx;
#else poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline )
return (poolhandle_t)pool; {
#endif mempool_t *pool;
size_t i;
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( pool->filename == NULL )
return Mem_InitPool( pool, name, filename, fileline );
}
pool = (mempool_t *)Q_realloc( poolchain, sizeof( *poolchain ) * ( poolcount + 1 ));
if( pool == NULL )
{
Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline );
return 0;
}
poolchain = pool;
pool = &poolchain[poolcount++];
return Mem_InitPool( pool, name, filename, fileline );
} }
void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline ) void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline )
{ {
mempool_t *pool; mempool_t *pool;
mempool_t **chainaddress;
if( *poolptr && ( pool = Mem_FindPool( *poolptr ))) if( *poolptr && ( pool = Mem_FindPool( *poolptr )))
{ {
// unlink pool from chain if( !pool->filename )
for( chainaddress = &poolchain; *chainaddress && *chainaddress != pool; chainaddress = &((*chainaddress)->next)); {
if( *chainaddress != pool ) Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline ); Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
*poolptr = 0;
return;
}
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline ); Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
*chainaddress = pool->next;
// free memory owned by the pool // free memory owned by the pool
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline ); while( pool->chain )
Mem_FreeBlock( pool->chain, filename, fileline );
// free the pool itself // free the pool itself
memset( pool, 0xBF, sizeof( mempool_t )); memset( pool, 0xBF, sizeof( mempool_t ));
Q_free( pool ); pool->chain = NULL;
pool->filename = NULL; // mark as reusable
*poolptr = 0; *poolptr = 0;
} }
} }
@ -396,7 +395,11 @@ void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline )
void _Mem_EmptyPool( poolhandle_t poolptr, const char *filename, int fileline ) void _Mem_EmptyPool( poolhandle_t poolptr, const char *filename, int fileline )
{ {
mempool_t *pool = Mem_FindPool( poolptr ); mempool_t *pool = Mem_FindPool( poolptr );
if( !poolptr ) Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline ); if( !poolptr )
{
Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
return;
}
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline ); Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
@ -421,7 +424,8 @@ static qboolean Mem_CheckAlloc( mempool_t *pool, void *data )
else else
{ {
// search all pools // search all pools
for( pool = poolchain; pool; pool = pool->next ) size_t i;
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{ {
if( Mem_CheckAlloc( pool, data )) if( Mem_CheckAlloc( pool, data ))
return true; return true;
@ -449,23 +453,27 @@ void _Mem_Check( const char *filename, int fileline )
{ {
memheader_t *mem; memheader_t *mem;
mempool_t *pool; mempool_t *pool;
size_t i;
for( pool = poolchain; pool; pool = pool->next ) for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
Mem_CheckPool( "Mem_CheckSentinels", pool, filename, fileline ); Mem_CheckPool( "Mem_CheckSentinels", pool, filename, fileline );
for( pool = poolchain; pool; pool = pool->next ) for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
for( mem = pool->chain; mem; mem = mem->next ) for( mem = pool->chain; mem; mem = mem->next )
Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline ); Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline );
} }
void Mem_PrintStats( void ) void Mem_PrintStats( void )
{ {
size_t count = 0, size = 0, realsize = 0; size_t count = 0, size = 0, realsize = 0, i;
mempool_t *pool; mempool_t *pool;
Mem_Check(); Mem_Check();
for( pool = poolchain; pool; pool = pool->next ) for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{ {
if( !pool->filename )
continue;
count++; count++;
size += pool->totalsize; size += pool->totalsize;
realsize += pool->realsize; realsize += pool->realsize;
@ -479,15 +487,19 @@ void Mem_PrintList( size_t minallocationsize )
{ {
mempool_t *pool; mempool_t *pool;
memheader_t *mem; memheader_t *mem;
size_t i;
Mem_Check(); Mem_Check();
Con_Printf( "memory pool list:\n" ); Con_Printf( "memory pool list:\n" );
Con_Printf( "\t^3size\t\t\t\tname\n"); Con_Printf( "\t^3size\t\t\t\tname\n");
for( pool = poolchain; pool; pool = pool->next ) for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{ {
long changed_size = (long)pool->totalsize - (long)pool->lastchecksize; long changed_size = (long)pool->totalsize - (long)pool->lastchecksize;
if( !pool->filename )
continue;
// poolnames can contain color symbols, make sure what color is reset // poolnames can contain color symbols, make sure what color is reset
if( pool->lastchecksize != 0 && changed_size != 0 ) if( pool->lastchecksize != 0 && changed_size != 0 )
{ {