From e81b5144b38972fa1544227a92863acc19fe141f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 18 Apr 2024 19:45:54 +0300 Subject: [PATCH] engine: common: zone: use realloc for managing mem pools rearrange data in memheader_s to avoid unnecessary paddings --- common/xash3d_types.h | 4 -- engine/common/zone.c | 134 +++++++++++++++++++++++------------------- 2 files changed, 73 insertions(+), 65 deletions(-) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 7eec99ba..1da13c34 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -28,11 +28,7 @@ typedef byte rgb_t[3]; // unsigned byte colorpack typedef vec_t matrix3x4[3][4]; typedef vec_t matrix4x4[4][4]; -#if XASH_64BIT typedef uint32_t poolhandle_t; -#else -typedef void* poolhandle_t; -#endif #undef true #undef false diff --git a/engine/common/zone.c b/engine/common/zone.c index 10107a4a..ec3344c7 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -55,9 +55,9 @@ typedef struct memheader_s { struct memheader_s *next; // next and previous memheaders in chain belonging to pool 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 + size_t size; // size of the memory after the header (excluding header and sentinel2) + poolhandle_t poolptr; // pool this memheader belongs to int fileline; #if !XASH_64BIT 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 realsize; // total memory allocated in this pool (actual malloc total) 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 int fileline; -#if XASH_64BIT - poolhandle_t idx; -#endif char name[64]; // name of the pool uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1 } mempool_t; 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 // which makes engine incompatible with 64-bit pointers I changed mempool type // 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 ) { - mempool_t *pool; - - for( pool = poolchain; pool; pool = pool->next ) - { - if( pool->idx == poolptr ) - return pool; - } + if( likely( poolptr > 0 && poolptr <= poolcount )) + return &poolchain[poolptr - 1]; Sys_Error( "%s: not allocated or double freed pool %d", __FUNCTION__, poolptr ); - 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 ) { @@ -132,7 +118,7 @@ static inline void Mem_PoolLinkAlloc( mempool_t *pool, memheader_t *mem ) if( mem->next ) mem->next->prev = mem; pool->chain = mem; mem->prev = NULL; - mem->pool = pool; + mem->poolptr = Mem_PoolIndex( pool ); } 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->prev ) mem->prev->next = 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 ) @@ -243,7 +229,7 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline )) return; - pool = mem->pool; + pool = Mem_FindPool( mem->poolptr ); // unlink memheader from doubly linked list 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 // (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 ); } 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 )); } -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; - - 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 )); + memset( pool, 0, sizeof( *pool )); // fill header pool->sentinel1 = MEMHEADER_SENTINEL1; pool->sentinel2 = MEMHEADER_SENTINEL1; pool->filename = filename; pool->fileline = fileline; - pool->chain = NULL; - pool->totalsize = 0; pool->realsize = sizeof( mempool_t ); Q_strncpy( pool->name, name, sizeof( pool->name )); - pool->next = poolchain; - poolchain = pool; -#if XASH_64BIT - pool->idx = ++lastidx; - return pool->idx; -#else - return (poolhandle_t)pool; -#endif + return Mem_PoolIndex( pool ); +} + +poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline ) +{ + 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 ) { mempool_t *pool; - mempool_t **chainaddress; if( *poolptr && ( pool = Mem_FindPool( *poolptr ))) { - // unlink pool from chain - 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 ); + if( !pool->filename ) + { + Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline ); + *poolptr = 0; + return; + } + Mem_CheckPool( "Mem_FreePool", pool, filename, fileline ); - *chainaddress = pool->next; // 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 memset( pool, 0xBF, sizeof( mempool_t )); - Q_free( pool ); + pool->chain = NULL; + pool->filename = NULL; // mark as reusable *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 ) { 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 ); @@ -421,7 +424,8 @@ static qboolean Mem_CheckAlloc( mempool_t *pool, void *data ) else { // 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 )) return true; @@ -449,23 +453,27 @@ void _Mem_Check( const char *filename, int fileline ) { memheader_t *mem; 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 ); - for( pool = poolchain; pool; pool = pool->next ) + for( i = 0, pool = poolchain; i < poolcount; i++, pool++ ) for( mem = pool->chain; mem; mem = mem->next ) Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline ); } void Mem_PrintStats( void ) { - size_t count = 0, size = 0, realsize = 0; + size_t count = 0, size = 0, realsize = 0, i; mempool_t *pool; Mem_Check(); - for( pool = poolchain; pool; pool = pool->next ) + for( i = 0, pool = poolchain; i < poolcount; i++, pool++ ) { + if( !pool->filename ) + continue; + count++; size += pool->totalsize; realsize += pool->realsize; @@ -479,15 +487,19 @@ void Mem_PrintList( size_t minallocationsize ) { mempool_t *pool; memheader_t *mem; + size_t i; Mem_Check(); Con_Printf( "memory pool list:\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; + if( !pool->filename ) + continue; + // poolnames can contain color symbols, make sure what color is reset if( pool->lastchecksize != 0 && changed_size != 0 ) {