From 4cab5b90e6379bc953e0df98693e0e985198d0f5 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Wed, 28 Mar 2007 13:09:49 +0000 Subject: [PATCH] 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. --- include/QF/zone.h | 2 +- libs/util/zone.c | 496 +++++++++++++++++++++++----------------------- 2 files changed, 252 insertions(+), 246 deletions(-) diff --git a/include/QF/zone.h b/include/QF/zone.h index 118a11105..713a10203 100644 --- a/include/QF/zone.h +++ b/include/QF/zone.h @@ -135,8 +135,8 @@ void *Cache_Alloc (cache_user_t *c, int size, const char *name); void Cache_Report (void); void Cache_Add (cache_user_t *c, void *object, cache_loader_t loader); void Cache_Remove (cache_user_t *c); -void *Cache_Get (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); int Cache_ReadLock (cache_user_t *c); diff --git a/libs/util/zone.c b/libs/util/zone.c index 9bc157ab0..7665f10d1 100644 --- a/libs/util/zone.c +++ b/libs/util/zone.c @@ -50,7 +50,8 @@ static __attribute__ ((used)) const char rcsid[] = #include "compat.h" 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 HUNK_SENTINAL 0x1df001ed @@ -405,6 +406,30 @@ Hunk_Print (qboolean all) 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 * 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); - 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 ("Not enough RAM allocated. Try starting using \"-mem 16\" on " "the %s command line. (%d - %d - %d < %d)", PROGRAM, hunk_size, hunk_low_used, hunk_high_used, size); + } h = (hunk_t *) (hunk_base + hunk_low_used); hunk_low_used += size; @@ -460,37 +491,11 @@ Hunk_FreeToLowMark (int 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 * -Hunk_HighAllocName (int size, const char *name) +Hunk_HighAlloc (int size) { - hunk_t *h; - if (size < 0) - Sys_Error ("Hunk_HighAllocName: bad size: %i", size); + Sys_Error ("Hunk_HighAlloc: bad size: %i", size); if (hunk_tempactive) { Hunk_FreeToHighMark (hunk_tempmark); @@ -500,7 +505,7 @@ Hunk_HighAllocName (int size, const char *name) Hunk_Check (); #endif - size = sizeof (hunk_t) + ((size + 15) & ~15); + size = ((size + 15) & ~15); if (hunk_size - hunk_low_used - hunk_high_used < 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; - Cache_FreeHigh (hunk_high_used); - h = (hunk_t *) (hunk_base + hunk_size - hunk_high_used); - - h->size = size; - h->sentinal = HUNK_SENTINAL; - strncpy (h->name, name, 8); - - return (void *) (h + 1); + return (void *) (hunk_base + hunk_size - hunk_high_used); } /* @@ -541,7 +539,7 @@ Hunk_TempAlloc (int size) hunk_tempmark = Hunk_HighMark (); - buf = Hunk_HighAllocName (size, "temp"); + buf = Hunk_HighAlloc (size); hunk_tempactive = true; @@ -550,19 +548,19 @@ Hunk_TempAlloc (int size) /* CACHE MEMORY */ -typedef struct cache_system_s { - cache_user_t *user; +typedef struct cache_system_s cache_system_t; +struct cache_system_s { + cache_system_t *prev, *next; + cache_system_t *lru_prev, *lru_next; // for LRU flushing char name[16]; - int size; // including this header + size_t size; // including this header int readlock; - struct cache_system_s *prev, *next; - struct cache_system_s *lru_prev, *lru_next; // for LRU flushing -} cache_system_t; + cache_user_t *user; +}; static cache_system_t cache_head; -static cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); -static void Cache_Profile (void); +static cache_system_t *Cache_TryAlloc (size_t size, qboolean nobottom); static void Cache_Move (cache_system_t * c) @@ -597,46 +595,22 @@ Cache_FreeLow (int new_low_hunk) cache_system_t *c; while (1) { - c = cache_head.next; + c = cache_head.prev; if (c == &cache_head) return; // nothing in cache at all if ((byte *) c >= hunk_base + new_low_hunk) return; // there is space to grow the hunk + Sys_Error ("FIXME: Cache_FreeLow: not enough memory"); 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 Cache_UnlinkLRU (cache_system_t * cs) { 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_prev->lru_next = cs->lru_next; @@ -648,7 +622,8 @@ static inline void Cache_MakeLRU (cache_system_t * cs) { 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; cs->lru_next = cache_head.lru_next; @@ -656,12 +631,27 @@ Cache_MakeLRU (cache_system_t * 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 Cache_FreeLRU (void) { 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) return 0; @@ -669,6 +659,16 @@ Cache_FreeLRU (void) 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 @@ -676,195 +676,58 @@ Cache_FreeLRU (void) Size should already include the header and padding */ static cache_system_t * -Cache_TryAlloc (int size, qboolean nobottom) +Cache_TryAlloc (size_t size, qboolean nobottom) { cache_system_t *cs, *new; // is the cache completely empty? - if (!nobottom && cache_head.prev == &cache_head) { - if (hunk_size - hunk_high_used - hunk_low_used < size) { - Sys_Printf ("Cache_TryAlloc: %i is greater then free hunk", size); - return NULL; - } - - new = (cache_system_t *) (hunk_base + hunk_low_used); - memset (new, 0, sizeof (*new)); + new = (cache_system_t *) Hunk_HighAlloc (size); + if (!new) + return 0; + memset (new, 0, size); new->size = size; - cache_head.prev = cache_head.next = new; new->prev = new->next = &cache_head; - Cache_MakeLRU (new); return new; } - // search from the bottom up for space - - new = (cache_system_t *) (hunk_base + hunk_low_used); - cs = cache_head.next; - - do { - if (!nobottom || cs != cache_head.next) { - if ((byte *) cs - (byte *) new >= size) { // found space - memset (new, 0, sizeof (*new)); + // search for space in existing cache + for (cs = cache_head.next; cs != &cache_head; cs = cs->next) { + if (cs->user) + continue; // block isn't free + if (cs->size >= size) { + // found a big enough free block. If possible, carve it up for + // later reuse, using the upper portion of the block for the + // newly allocated block. + 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->next = cs; - new->prev = cs->prev; - cs->prev->next = new; - cs->prev = new; - - Cache_MakeLRU (new); - - return new; + cs->size -= size; + link_cache_system (new, cs); } + Cache_MakeLRU (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 - if (hunk_base + hunk_size - hunk_high_used - (byte *) new >= size) { - memset (new, 0, sizeof (*new)); + // didn't find a free block, so make a new one. + new = Hunk_HighAlloc (size); + if (new) { + memset (new, 0, size); new->size = size; - - new->next = &cache_head; - new->prev = cache_head.prev; - cache_head.prev->next = new; - cache_head.prev = new; - + link_cache_system (new, &cache_head); Cache_MakeLRU (new); - return new; } - return NULL; // 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); + return 0; // couldn't allocate } static void @@ -900,6 +763,149 @@ Cache_Profile (void) " %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 Cache_Add (cache_user_t *c, void *object, cache_loader_t loader) {