new cache allocation scheme. still a couple warts, but at least now there's something to work with, and more importantly, there won't be cache movement anywhere near as often.

This commit is contained in:
Bill Currie 2007-03-28 13:09:49 +00:00 committed by Jeff Teunissen
parent e5b972529b
commit 4cab5b90e6
2 changed files with 252 additions and 246 deletions

View file

@ -135,8 +135,8 @@ void *Cache_Alloc (cache_user_t *c, int size, const char *name);
void Cache_Report (void); void Cache_Report (void);
void Cache_Add (cache_user_t *c, void *object, cache_loader_t loader); void Cache_Add (cache_user_t *c, void *object, cache_loader_t loader);
void Cache_Remove (cache_user_t *c); void Cache_Remove (cache_user_t *c);
void *Cache_Get (cache_user_t *c);
void *Cache_TryGet (cache_user_t *c); void *Cache_TryGet (cache_user_t *c);
void *Cache_Get (cache_user_t *c);
void Cache_Release (cache_user_t *c); void Cache_Release (cache_user_t *c);
int Cache_ReadLock (cache_user_t *c); int Cache_ReadLock (cache_user_t *c);

View file

@ -50,7 +50,8 @@ static __attribute__ ((used)) const char rcsid[] =
#include "compat.h" #include "compat.h"
static void Cache_FreeLow (int new_low_hunk); static void Cache_FreeLow (int new_low_hunk);
static void Cache_FreeHigh (int new_high_hunk); static void Cache_Profile (void);
static qboolean Cache_FreeLRU (void);
#define ZONEID 0x1d4a11 #define ZONEID 0x1d4a11
#define HUNK_SENTINAL 0x1df001ed #define HUNK_SENTINAL 0x1df001ed
@ -405,6 +406,30 @@ Hunk_Print (qboolean all)
Sys_Printf ("%8i total blocks\n", totalblocks); Sys_Printf ("%8i total blocks\n", totalblocks);
} }
*/ */
static void
Hunk_FreeToHighMark (int mark)
{
if (hunk_tempactive) {
hunk_tempactive = false;
Hunk_FreeToHighMark (hunk_tempmark);
}
if (mark < 0 || mark > hunk_high_used)
Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark);
memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
hunk_high_used = mark;
}
static int
Hunk_HighMark (void)
{
if (hunk_tempactive) {
hunk_tempactive = false;
Hunk_FreeToHighMark (hunk_tempmark);
}
return hunk_high_used;
}
VISIBLE void * VISIBLE void *
Hunk_AllocName (int size, const char *name) Hunk_AllocName (int size, const char *name)
{ {
@ -419,11 +444,17 @@ Hunk_AllocName (int size, const char *name)
size = sizeof (hunk_t) + ((size + 15) & ~15); size = sizeof (hunk_t) + ((size + 15) & ~15);
if (hunk_size - hunk_low_used - hunk_high_used < size) if (hunk_size - hunk_low_used - hunk_high_used < size) {
Hunk_HighMark();
Cache_FreeLRU ();
}
if (hunk_size - hunk_low_used - hunk_high_used < size) {
Cache_Profile ();
Sys_Error Sys_Error
("Not enough RAM allocated. Try starting using \"-mem 16\" on " ("Not enough RAM allocated. Try starting using \"-mem 16\" on "
"the %s command line. (%d - %d - %d < %d)", "the %s command line. (%d - %d - %d < %d)",
PROGRAM, hunk_size, hunk_low_used, hunk_high_used, size); PROGRAM, hunk_size, hunk_low_used, hunk_high_used, size);
}
h = (hunk_t *) (hunk_base + hunk_low_used); h = (hunk_t *) (hunk_base + hunk_low_used);
hunk_low_used += size; hunk_low_used += size;
@ -460,37 +491,11 @@ Hunk_FreeToLowMark (int mark)
hunk_low_used = mark; hunk_low_used = mark;
} }
static void
Hunk_FreeToHighMark (int mark)
{
if (hunk_tempactive) {
hunk_tempactive = false;
Hunk_FreeToHighMark (hunk_tempmark);
}
if (mark < 0 || mark > hunk_high_used)
Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark);
memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark);
hunk_high_used = mark;
}
static int
Hunk_HighMark (void)
{
if (hunk_tempactive) {
hunk_tempactive = false;
Hunk_FreeToHighMark (hunk_tempmark);
}
return hunk_high_used;
}
static void * static void *
Hunk_HighAllocName (int size, const char *name) Hunk_HighAlloc (int size)
{ {
hunk_t *h;
if (size < 0) if (size < 0)
Sys_Error ("Hunk_HighAllocName: bad size: %i", size); Sys_Error ("Hunk_HighAlloc: bad size: %i", size);
if (hunk_tempactive) { if (hunk_tempactive) {
Hunk_FreeToHighMark (hunk_tempmark); Hunk_FreeToHighMark (hunk_tempmark);
@ -500,7 +505,7 @@ Hunk_HighAllocName (int size, const char *name)
Hunk_Check (); Hunk_Check ();
#endif #endif
size = sizeof (hunk_t) + ((size + 15) & ~15); size = ((size + 15) & ~15);
if (hunk_size - hunk_low_used - hunk_high_used < size) { if (hunk_size - hunk_low_used - hunk_high_used < size) {
Sys_Printf ("Hunk_HighAlloc: failed on %i bytes\n", size); Sys_Printf ("Hunk_HighAlloc: failed on %i bytes\n", size);
@ -508,15 +513,8 @@ Hunk_HighAllocName (int size, const char *name)
} }
hunk_high_used += size; hunk_high_used += size;
Cache_FreeHigh (hunk_high_used);
h = (hunk_t *) (hunk_base + hunk_size - hunk_high_used); return (void *) (hunk_base + hunk_size - hunk_high_used);
h->size = size;
h->sentinal = HUNK_SENTINAL;
strncpy (h->name, name, 8);
return (void *) (h + 1);
} }
/* /*
@ -541,7 +539,7 @@ Hunk_TempAlloc (int size)
hunk_tempmark = Hunk_HighMark (); hunk_tempmark = Hunk_HighMark ();
buf = Hunk_HighAllocName (size, "temp"); buf = Hunk_HighAlloc (size);
hunk_tempactive = true; hunk_tempactive = true;
@ -550,19 +548,19 @@ Hunk_TempAlloc (int size)
/* CACHE MEMORY */ /* CACHE MEMORY */
typedef struct cache_system_s { typedef struct cache_system_s cache_system_t;
cache_user_t *user; struct cache_system_s {
cache_system_t *prev, *next;
cache_system_t *lru_prev, *lru_next; // for LRU flushing
char name[16]; char name[16];
int size; // including this header size_t size; // including this header
int readlock; int readlock;
struct cache_system_s *prev, *next; cache_user_t *user;
struct cache_system_s *lru_prev, *lru_next; // for LRU flushing };
} cache_system_t;
static cache_system_t cache_head; static cache_system_t cache_head;
static cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); static cache_system_t *Cache_TryAlloc (size_t size, qboolean nobottom);
static void Cache_Profile (void);
static void static void
Cache_Move (cache_system_t * c) Cache_Move (cache_system_t * c)
@ -597,46 +595,22 @@ Cache_FreeLow (int new_low_hunk)
cache_system_t *c; cache_system_t *c;
while (1) { while (1) {
c = cache_head.next; c = cache_head.prev;
if (c == &cache_head) if (c == &cache_head)
return; // nothing in cache at all return; // nothing in cache at all
if ((byte *) c >= hunk_base + new_low_hunk) if ((byte *) c >= hunk_base + new_low_hunk)
return; // there is space to grow the hunk return; // there is space to grow the hunk
Sys_Error ("FIXME: Cache_FreeLow: not enough memory");
Cache_Move (c); // reclaim the space Cache_Move (c); // reclaim the space
} }
} }
/*
Cache_FreeHigh
Throw things out until the hunk can be expanded to the given point
*/
static void
Cache_FreeHigh (int new_high_hunk)
{
cache_system_t *c, *prev;
prev = NULL;
while (1) {
c = cache_head.prev;
if (c == &cache_head)
return; // nothing in cache at all
if ((byte *) c + c->size <= hunk_base + hunk_size - new_high_hunk)
return; // there is space to grow the hunk
if (c == prev)
Cache_Free (c->user); // didn't move out of the way
else {
Cache_Move (c); // try to move it
prev = c;
}
}
}
static inline void static inline void
Cache_UnlinkLRU (cache_system_t * cs) Cache_UnlinkLRU (cache_system_t * cs)
{ {
if (!cs->lru_next || !cs->lru_prev) if (!cs->lru_next || !cs->lru_prev)
Sys_Error ("Cache_UnlinkLRU: NULL link"); Sys_Error ("Cache_UnlinkLRU: NULL link: %s %p %p",
cs->name, cs->lru_next, cs->lru_prev);
cs->lru_next->lru_prev = cs->lru_prev; cs->lru_next->lru_prev = cs->lru_prev;
cs->lru_prev->lru_next = cs->lru_next; cs->lru_prev->lru_next = cs->lru_next;
@ -648,7 +622,8 @@ static inline void
Cache_MakeLRU (cache_system_t * cs) Cache_MakeLRU (cache_system_t * cs)
{ {
if (cs->lru_next || cs->lru_prev) if (cs->lru_next || cs->lru_prev)
Sys_Error ("Cache_MakeLRU: active link"); Sys_Error ("Cache_MakeLRU: active link: %s %p %p",
cs->name, cs->lru_next, cs->lru_prev);
cache_head.lru_next->lru_prev = cs; cache_head.lru_next->lru_prev = cs;
cs->lru_next = cache_head.lru_next; cs->lru_next = cache_head.lru_next;
@ -656,12 +631,27 @@ Cache_MakeLRU (cache_system_t * cs)
cache_head.lru_next = cs; cache_head.lru_next = cs;
} }
static void
check_cache (void)
{
cache_system_t *cs;
for (cs = cache_head.prev; cs != &cache_head; cs = cs->prev)
if (cs->prev != &cache_head
&& (byte *) cs + cs->size != (byte *) cs->prev)
Sys_Error ("inconsistent cache %p %p %d %d", cs, cs->prev,
(int)cs->size,
(int) ((char *)cs->prev - (char *)cs));
}
static qboolean static qboolean
Cache_FreeLRU (void) Cache_FreeLRU (void)
{ {
cache_system_t *cs; cache_system_t *cs;
for (cs = cache_head.lru_prev; cs->readlock; cs = cs->lru_prev) check_cache ();
for (cs = cache_head.lru_prev;
cs != &cache_head && cs->readlock; cs = cs->lru_prev)
; ;
if (cs == &cache_head) if (cs == &cache_head)
return 0; return 0;
@ -669,6 +659,16 @@ Cache_FreeLRU (void)
return 1; return 1;
} }
static void
link_cache_system (cache_system_t *new, cache_system_t *cs)
{
new->next = cs;
new->prev = cs->prev;
cs->prev->next = new;
cs->prev = new;
}
/* /*
Cache_TryAlloc Cache_TryAlloc
@ -676,195 +676,58 @@ Cache_FreeLRU (void)
Size should already include the header and padding Size should already include the header and padding
*/ */
static cache_system_t * static cache_system_t *
Cache_TryAlloc (int size, qboolean nobottom) Cache_TryAlloc (size_t size, qboolean nobottom)
{ {
cache_system_t *cs, *new; cache_system_t *cs, *new;
// is the cache completely empty? // is the cache completely empty?
if (!nobottom && cache_head.prev == &cache_head) { if (!nobottom && cache_head.prev == &cache_head) {
if (hunk_size - hunk_high_used - hunk_low_used < size) { new = (cache_system_t *) Hunk_HighAlloc (size);
Sys_Printf ("Cache_TryAlloc: %i is greater then free hunk", size); if (!new)
return NULL; return 0;
} memset (new, 0, size);
new = (cache_system_t *) (hunk_base + hunk_low_used);
memset (new, 0, sizeof (*new));
new->size = size; new->size = size;
cache_head.prev = cache_head.next = new; cache_head.prev = cache_head.next = new;
new->prev = new->next = &cache_head; new->prev = new->next = &cache_head;
Cache_MakeLRU (new); Cache_MakeLRU (new);
return new; return new;
} }
// search from the bottom up for space // search for space in existing cache
for (cs = cache_head.next; cs != &cache_head; cs = cs->next) {
new = (cache_system_t *) (hunk_base + hunk_low_used); if (cs->user)
cs = cache_head.next; continue; // block isn't free
if (cs->size >= size) {
do { // found a big enough free block. If possible, carve it up for
if (!nobottom || cs != cache_head.next) { // later reuse, using the upper portion of the block for the
if ((byte *) cs - (byte *) new >= size) { // found space // newly allocated block.
memset (new, 0, sizeof (*new)); new = cs;
if (size - cs->size >= sizeof (cache_system_t)) {
new = (cache_system_t *) ((char *) cs + cs->size - size);
memset (new, 0, size);
new->size = size; new->size = size;
cs->size -= size;
new->next = cs; link_cache_system (new, cs);
new->prev = cs->prev; }
cs->prev->next = new;
cs->prev = new;
Cache_MakeLRU (new); Cache_MakeLRU (new);
return new; return new;
} }
} }
// continue looking
new = (cache_system_t *) ((byte *) cs + cs->size);
cs = cs->next;
} while (cs != &cache_head); if (nobottom)
return 0;
// try to allocate one at the very end // didn't find a free block, so make a new one.
if (hunk_base + hunk_size - hunk_high_used - (byte *) new >= size) { new = Hunk_HighAlloc (size);
memset (new, 0, sizeof (*new)); if (new) {
memset (new, 0, size);
new->size = size; new->size = size;
link_cache_system (new, &cache_head);
new->next = &cache_head;
new->prev = cache_head.prev;
cache_head.prev->next = new;
cache_head.prev = new;
Cache_MakeLRU (new); Cache_MakeLRU (new);
return new; return new;
} }
return NULL; // couldn't allocate return 0; // couldn't allocate
}
/*
Cache_Flush
Throw everything out, so new data will be demand cached
*/
void
Cache_Flush (void)
{
while (cache_head.next != &cache_head) {
if (!cache_head.next->user->data)
Sys_Error ("Cache_Flush: user/system out of sync for "
"'%s' with %d size",
cache_head.next->name, cache_head.next->size);
Cache_Free (cache_head.next->user); // reclaim the space
}
}
static void
Cache_Print (void)
{
cache_system_t *cd;
for (cd = cache_head.next; cd != &cache_head; cd = cd->next) {
Sys_Printf ("%8i : %s\n", cd->size, cd->name);
}
}
VISIBLE void
Cache_Report (void)
{
Sys_DPrintf ("%4.1f megabyte data cache\n",
(hunk_size - hunk_high_used -
hunk_low_used) / (float) (1024 * 1024));
}
static void
Cache_Init (void)
{
cache_head.next = cache_head.prev = &cache_head;
cache_head.lru_next = cache_head.lru_prev = &cache_head;
Cmd_AddCommand ("cache_flush", Cache_Flush, "Clears the current game "
"cache");
Cmd_AddCommand ("cache_profile", Cache_Profile, "Prints a profile of "
"the current cache");
Cmd_AddCommand ("cache_print", Cache_Print, "Prints out items in the "
"cache");
}
/*
Cache_Free
Frees the memory and removes it from the LRU list
*/
VISIBLE void
Cache_Free (cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
Sys_Error ("Cache_Free: not allocated");
cs = ((cache_system_t *) c->data) - 1;
Sys_DPrintf ("Cache_Free: freeing '%s'\n", cs->name);
cs->prev->next = cs->next;
cs->next->prev = cs->prev;
cs->next = cs->prev = NULL;
c->data = NULL;
Cache_UnlinkLRU (cs);
}
VISIBLE void *
Cache_Check (cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
return NULL;
cs = ((cache_system_t *) c->data) - 1;
// move to head of LRU
Cache_UnlinkLRU (cs);
Cache_MakeLRU (cs);
return c->data;
}
VISIBLE void *
Cache_Alloc (cache_user_t *c, int size, const char *name)
{
cache_system_t *cs;
if (c->data)
Sys_Error ("Cache_Alloc: already allocated");
if (size <= 0)
Sys_Error ("Cache_Alloc: size %i", size);
size = (size + sizeof (cache_system_t) + 15) & ~15;
// find memory for it
while (1) {
cs = Cache_TryAlloc (size, false);
if (cs) {
strncpy (cs->name, name, sizeof (cs->name) - 1);
c->data = (void *) (cs + 1);
cs->user = c;
break;
}
// free the least recently used cachedat
if (!Cache_FreeLRU())
Sys_Error ("Cache_Alloc: out of memory");
}
return Cache_Check (c);
} }
static void static void
@ -900,6 +763,149 @@ Cache_Profile (void)
" %d per allocation\n", total, count, total / count); " %d per allocation\n", total, count, total / count);
} }
static void
Cache_Print (void)
{
cache_system_t *cd;
for (cd = cache_head.next; cd != &cache_head; cd = cd->next) {
Sys_Printf ("%8d : %s\n", (int) cd->size, cd->name);
}
}
static void
Cache_Init (void)
{
cache_head.next = cache_head.prev = &cache_head;
cache_head.lru_next = cache_head.lru_prev = &cache_head;
cache_head.user = (cache_user_t *) 1; // make it look allocated
cache_head.readlock = 1; // don't try to free or move it
Cmd_AddCommand ("cache_flush", Cache_Flush, "Clears the current game "
"cache");
Cmd_AddCommand ("cache_profile", Cache_Profile, "Prints a profile of "
"the current cache");
Cmd_AddCommand ("cache_print", Cache_Print, "Prints out items in the "
"cache");
}
/*
Cache_Flush
Throw everything out, so new data will be demand cached
*/
void
Cache_Flush (void)
{
while (cache_head.next != &cache_head) {
if (!cache_head.next->user->data)
Sys_Error ("Cache_Flush: user/system out of sync for "
"'%s' with %d size",
cache_head.next->name, (int) cache_head.next->size);
Cache_Free (cache_head.next->user); // reclaim the space
}
}
VISIBLE void *
Cache_Check (cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
return NULL;
cs = ((cache_system_t *) c->data) - 1;
// move to head of LRU
Cache_UnlinkLRU (cs);
Cache_MakeLRU (cs);
return c->data;
}
/*
Cache_Free
Frees the memory and removes it from the LRU list
*/
VISIBLE void
Cache_Free (cache_user_t *c)
{
cache_system_t *cs;
if (!c->data)
Sys_Error ("Cache_Free: not allocated");
cs = ((cache_system_t *) c->data) - 1;
if (cs->readlock)
Sys_Error ("Cache_Free: attempt to free locked block");
Sys_DPrintf ("Cache_Free: freeing '%s' %p\n", cs->name, cs);
Cache_UnlinkLRU (cs);
//check_cache ();
cs->user = 0;
if (!cs->prev->user) {
cs->size += cs->prev->size;
cs->prev->prev->next = cs;
cs->prev = cs->prev->prev;
}
if (!cs->next->user) {
cs = cs->next;
cs->size += cs->prev->size;
cs->prev->prev->next = cs;
cs->prev = cs->prev->prev;
}
if (cs->next == &cache_head) {
cs->next->prev = cs->prev;
cs->prev->next = cs->next;
Hunk_FreeToHighMark (hunk_high_used - cs->size);
}
//check_cache ();
c->data = NULL;
}
VISIBLE void *
Cache_Alloc (cache_user_t *c, int size, const char *name)
{
cache_system_t *cs;
if (c->data)
Sys_Error ("Cache_Alloc: already allocated");
if (size <= 0)
Sys_Error ("Cache_Alloc: size %i", size);
size = (size + sizeof (cache_system_t) + 15) & ~15;
// find memory for it
while (1) {
cs = Cache_TryAlloc (size, false);
if (cs) {
strncpy (cs->name, name, sizeof (cs->name) - 1);
c->data = (void *) (cs + 1);
cs->user = c;
break;
}
// free the least recently used cachedat
if (!Cache_FreeLRU())
Sys_Error ("Cache_Alloc: out of memory");
}
return Cache_Check (c);
}
VISIBLE void
Cache_Report (void)
{
Sys_DPrintf ("%4.1f megabyte data cache\n",
(hunk_size - hunk_high_used -
hunk_low_used) / (float) (1024 * 1024));
}
VISIBLE void VISIBLE void
Cache_Add (cache_user_t *c, void *object, cache_loader_t loader) Cache_Add (cache_user_t *c, void *object, cache_loader_t loader)
{ {