--------------------- PatchSet 1968 Date: 2001/04/18 18:17:43 Author: akroonmaa Branch: chunked_mempools Tag: (none) Log: added delete function to splay.c converted chunk search from linklist to splay tree added memPool total accounted to cachemgr Info page Indenting... Members: include/MemPool.h:1.1.2.3->1.1.2.4 include/splay.h:1.4->1.4.22.1 lib/MemPool.c:1.1.2.2->1.1.2.3 lib/splay.c:1.3->1.3.48.1 src/MemPoolStats.c:1.1.2.4->1.1.2.5 src/stat.c:1.9->1.9.8.1 Index: squid/include/MemPool.h =================================================================== RCS file: /cvsroot/squid-sf//squid/include/Attic/MemPool.h,v retrieving revision 1.1.2.3 retrieving revision 1.1.2.4 diff -u -r1.1.2.3 -r1.1.2.4 --- squid/include/MemPool.h 11 Apr 2001 10:07:18 -0000 1.1.2.3 +++ squid/include/MemPool.h 18 Apr 2001 18:17:43 -0000 1.1.2.4 @@ -5,12 +5,13 @@ #include "config.h" #include "Stack.h" #include "util.h" +#include "splay.h" #define MB ((size_t)1024*1024) #define MEM_PAGE_SIZE 4096 #define MEM_CHUNK_SIZE 4096 * 4 -#define MEM_CHUNK_MAX_SIZE 2048 * 1024 /* 2MB */ +#define MEM_CHUNK_MAX_SIZE 256 * 1024 /* 2MB */ #define MEM_MIN_FREE 32 #define MEM_MAX_FREE 65535 /* ushort is max number of items per chunk */ @@ -21,16 +22,16 @@ extern void memArrayInit(Array * s); extern void memArrayClean(Array * s); -extern void memArrayAppend(Array *s, void *obj); -extern void memArrayDestroy(Array *s); +extern void memArrayAppend(Array * s, void *obj); +extern void memArrayDestroy(Array * s); #define CHUNKS 1 /* object to track per-action memory usage (e.g. #idle objects) */ struct _MemMeter { - ssize_t level; /* current level (count or volume) */ - ssize_t hwater_level; /* high water mark */ - time_t hwater_stamp; /* timestamp of last high water mark change */ + ssize_t level; /* current level (count or volume) */ + ssize_t hwater_level; /* high water mark */ + time_t hwater_stamp; /* timestamp of last high water mark change */ }; /* object to track per-pool memory usage (alloc = inuse+idle) */ @@ -56,6 +57,7 @@ MemChunk *nextbest; MemChunk *lastchunk; MemPoolMeter meter; + splayNode *allChunks; }; struct _MemChunk { @@ -66,6 +68,7 @@ MemChunk *prev; time_t lastref; }; + #else /* a pool is a [growing] space for objects of the same size */ struct _MemPool { @@ -74,6 +77,7 @@ Stack pstack; /* stack for free pointers */ MemPoolMeter meter; }; + #endif #define SIZEOF_CHUNK ( ( sizeof(MemChunk) + sizeof(double) -1) / sizeof(double) ) * sizeof(double); Index: squid/include/splay.h =================================================================== RCS file: /cvsroot/squid-sf//squid/include/splay.h,v retrieving revision 1.4 retrieving revision 1.4.22.1 diff -u -r1.4 -r1.4.22.1 --- squid/include/splay.h 7 Jan 2001 15:24:00 -0000 1.4 +++ squid/include/splay.h 18 Apr 2001 18:17:43 -0000 1.4.22.1 @@ -1,5 +1,5 @@ /* - * $Id: splay.h,v 1.4 2001/01/07 15:24:00 hno Exp $ + * $Id: splay.h,v 1.4.22.1 2001/04/18 18:17:43 akroonmaa Exp $ */ #ifndef _SPLAY_H @@ -19,6 +19,7 @@ extern splayNode *splay_insert(void *, splayNode *, SPLAYCMP *); extern splayNode *splay_splay(const void *, splayNode *, SPLAYCMP *); +extern splayNode *splay_delete(const void *, splayNode *, SPLAYCMP *); extern void splay_destroy(splayNode *, SPLAYFREE *); extern void splay_walk(splayNode *, SPLAYWALKEE *, void *); Index: squid/lib/MemPool.c =================================================================== RCS file: /cvsroot/squid-sf//squid/lib/Attic/MemPool.c,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -r1.1.2.2 -r1.1.2.3 --- squid/lib/MemPool.c 9 Apr 2001 11:36:34 -0000 1.1.2.2 +++ squid/lib/MemPool.c 18 Apr 2001 18:17:43 -0000 1.1.2.3 @@ -34,65 +34,66 @@ */ /* - Old way: - xmalloc each item separately, upon free stack into idle pool array. - each item is individually malloc()ed from system, imposing libmalloc - overhead, and additionally we add our overhead of pointer size per item - as we keep a list of pointer to free items. - - Chunking: - xmalloc Chunk that fits at least MEM_MIN_FREE (32) items in an array, but - limit Chunk size to MEM_CHUNK_MAX_SIZE (256K). Chunk size is rounded up to - MEM_PAGE_SIZE (4K), trying to have chunks in multiples of VM_PAGE size. - Minimum Chunk size is MEM_CHUNK_SIZE (16K). - A number of items fits into a single chunk, depending on item size. - Maximum number of items per chunk is limited to MEM_MAX_FREE (65535). - - We populate Chunk with a linkedlist, each node at first word of item, - and pointing at next free item. Chunk->FreeList is pointing at first - free node. Thus we stuff free housekeeping into the Chunk itself, and - omit pointer overhead per item. - - Chunks are created on demand, and new chunks are inserted into linklist - of chunks so that Chunks with smaller pointer value are placed closer - to the linklist head. Head is a hotspot, servicing most of requests, so - slow sorting occurs and Chunks in highest memory tend to become idle - and freeable. - - event is registered that runs every 15 secs and checks reference time - of each idle chunk. If a chunk is not referenced for 15 secs, it is - released. - - If mem_idle_limit is exceeded with pools, every chunk that becomes - idle is immediately considered for release, unless this is the only - chunk with free items in it. - - In cachemgr output, there are new columns for chunking. Special item, - Fragm, is shown to estimate approximately fragmentation of chunked - pools. Fragmentation is calculated by taking amount of items in use, - calculating needed amount of chunks to fit all, and then comparing to - actual amount of chunks in use. Fragm number, in percent, is showing - how many percent of chunks are in use excessively. 100% meaning that - twice the needed amount of chunks are in use. - - Andres Kroonmaa. + * Old way: + * xmalloc each item separately, upon free stack into idle pool array. + * each item is individually malloc()ed from system, imposing libmalloc + * overhead, and additionally we add our overhead of pointer size per item + * as we keep a list of pointer to free items. + * + * Chunking: + * xmalloc Chunk that fits at least MEM_MIN_FREE (32) items in an array, but + * limit Chunk size to MEM_CHUNK_MAX_SIZE (256K). Chunk size is rounded up to + * MEM_PAGE_SIZE (4K), trying to have chunks in multiples of VM_PAGE size. + * Minimum Chunk size is MEM_CHUNK_SIZE (16K). + * A number of items fits into a single chunk, depending on item size. + * Maximum number of items per chunk is limited to MEM_MAX_FREE (65535). + * + * We populate Chunk with a linkedlist, each node at first word of item, + * and pointing at next free item. Chunk->FreeList is pointing at first + * free node. Thus we stuff free housekeeping into the Chunk itself, and + * omit pointer overhead per item. + * + * Chunks are created on demand, and new chunks are inserted into linklist + * of chunks so that Chunks with smaller pointer value are placed closer + * to the linklist head. Head is a hotspot, servicing most of requests, so + * slow sorting occurs and Chunks in highest memory tend to become idle + * and freeable. + * + * event is registered that runs every 15 secs and checks reference time + * of each idle chunk. If a chunk is not referenced for 15 secs, it is + * released. + * + * If mem_idle_limit is exceeded with pools, every chunk that becomes + * idle is immediately considered for release, unless this is the only + * chunk with free items in it. + * + * In cachemgr output, there are new columns for chunking. Special item, + * Fragm, is shown to estimate approximately fragmentation of chunked + * pools. Fragmentation is calculated by taking amount of items in use, + * calculating needed amount of chunks to fit all, and then comparing to + * actual amount of chunks in use. Fragm number, in percent, is showing + * how many percent of chunks are in use excessively. 100% meaning that + * twice the needed amount of chunks are in use. + * + * Andres Kroonmaa. */ /* - Debug assist: - PARANOID - Scan freelist to detect double-freed and tampered free objects - PARANOID_LEVEL - skip pools with more than that items, for speedup - - DISABLE_POOLS - no mempooling, revert back to good-old xcalloc/xfree - might be useful when debugging memory issues. - - MEM_PROTECT - mprotect() freed pool objects that are pagealigned and - size of multiples of VMpage. Helps you to detect buffer writes after - being freed. Needs mmap support. Also, redefine in dlmalloc.c: - #define DEFAULT_MMAP_MAX (2048) - #define DEFAULT_MMAP_THRESHOLD (16 * 1024) -*/ + * Debug assist: + * PARANOID - Scan freelist to detect double-freed and tampered free objects + * PARANOID_LEVEL - skip pools with more than that items, for speedup + * + * DISABLE_POOLS - no mempooling, revert back to good-old xcalloc/xfree + * might be useful when debugging memory issues. + * + * MEM_PROTECT - mprotect() freed pool objects that are pagealigned and + * size of multiples of VMpage. Helps you to detect buffer writes after + * being freed. Needs mmap support. Also, redefine in dlmalloc.c: + * #define DEFAULT_MMAP_MAX (2048) + * #define DEFAULT_MMAP_THRESHOLD (16 * 1024) + */ +#define SPLAY 1 #define DISABLE_POOLS 0 #define PARANOID 0 /* NB: slows down alot! */ #define PARANOID_LEVEL 300 @@ -120,7 +121,8 @@ MemPoolMeter TheMemPools; MemPoolMeter TheChunks; MemPoolMeter TheMeter; -gb_t mem_traffic_volume = { 0, 0 }; +gb_t mem_traffic_volume = +{0, 0}; Array Pools; static int Pool_id_counter = 0; @@ -132,6 +134,24 @@ void memArrayAppend(Array * s, void *obj); void memArrayDestroy(Array * s); void memPoolClean(MemPool * pool, time_t maxage); +static MemPool *lastPool; + +int +memCompChunks(MemChunk * chunkA, MemChunk * chunkB) +{ + return chunkA->objCache - chunkB->objCache; +} + +int +memCompObjChunks(void *obj, MemChunk * chunk) +{ + bounds = obj - chunk->objCache; + if (bounds < 0) + return -1; + if (bounds < lastPool->chunk_size) + return 0; + return 1; +} MemChunk * memPoolChunkNew(MemPool * pool) @@ -141,12 +161,7 @@ MemChunk *chunk; chunk = xcalloc(1, sizeof(MemChunk)); /* should have a pool for this too */ -#if MEM_PROTECT - chunk->objCache = xvalloc(pool->chunk_size); - memset(chunk->objCache, 0, pool->chunk_size); -#else chunk->objCache = xcalloc(1, pool->chunk_size); -#endif Free = chunk->freeList = chunk->objCache; for (i = 1; i < pool->capacity; i++) { @@ -159,6 +174,8 @@ memMeterAdd(TheMeter.idle, pool->capacity * pool->obj_size); pool->chunkCount++; chunk->lastref = squid_curtime; + lastPool = pool; + pool->allChunks = splay_insert(chunk, pool->allChunks, (SPLAYCMP *) memCompChunks); return chunk; } @@ -172,10 +189,8 @@ pool->chunkCount--; if (pool->lastchunk == chunk) pool->lastchunk = chunk->prev; -#if PARANOID - if (pool->nextbest == chunk) /* shouldn't happen */ - pool->nextbest = NULL; -#endif + lastPool = pool; + pool->allChunks = splay_delete(chunk, pool->allChunks, (SPLAYCMP *) memCompChunks); xfree(chunk->objCache); xfree(chunk); } @@ -185,62 +200,31 @@ { MemChunk *chunk; void **Free; -#if PARANOID - void *last=NULL; - void *prelast=NULL; -#endif assert(pool->Chunks); - chunk = pool->Chunks; - while (1) { - bounds = obj - chunk->objCache; - if ((bounds >= 0) && (bounds < pool->chunk_size)) { /* does it belong to this chunk? */ -#if PARANOID - Free = chunk->freeList; -#if PARANOID_LEVEL - if (pool->meter.inuse.level < PARANOID_LEVEL) /* Omit larger pools for speedup */ -#endif - while (Free) { - assert(obj != Free); - bounds = (void *) Free - chunk->objCache; - assert(PARANOID && bounds >= 0 && bounds < pool->chunk_size); - prelast = last; - last = Free; /* assist debugging */ - Free = *Free; - } -#endif - assert(chunk->count > 0); - chunk->count--; + lastPool = pool; + pool->allChunks = splay_splay(obj, pool->allChunks, (SPLAYCMP *) memCompObjChunks); + if (splayLastResult == 0) { + chunk = pool->allChunks->data; + assert(chunk->count > 0); + chunk->count--; + if ((pool->obj_size % 2048) != 0) memset(obj, 0, pool->obj_size); - Free = obj; - *Free = chunk->freeList; -#if MEM_PROTECT - if ((pool->obj_size % MEM_PAGE_SIZE) == 0) { - if ( ( (int)obj % MEM_PAGE_SIZE) == 0) { - mprotect(obj, pool->obj_size, PROT_READ); - } - } -#endif - chunk->freeList = obj; - chunk->lastref = squid_curtime; - if (pool->nextbest) - if (chunk->objCache < pool->nextbest->objCache) /* prefer chunks in lowest ram */ - pool->nextbest = chunk; - if (TheMeter.idle.level <= mem_idle_limit) - return; + Free = obj; + *Free = chunk->freeList; + chunk->freeList = obj; + chunk->lastref = squid_curtime; + if (pool->nextbest) + if (chunk->objCache < pool->nextbest->objCache) /* prefer chunks in lowest ram */ + pool->nextbest = chunk; + if (TheMeter.idle.level > mem_idle_limit) { if (chunk->count == 0) memPoolClean(pool, 0); /* free chunk, over idle limits, so cleanup */ - return; } - if (chunk->next == NULL) - break; - chunk = chunk->next; + return; } /* obj does NOT belong to this pool!! */ - assert(pool->Chunks == obj); - /* not reached */ - - /* Perhaps we should assume we are over pool_limits and revert to xfree() ? */ - return; + assert("memPoolFree: pool->Chunks" == "obj does NOT belong to this pool!"); + return; /* not reached */ } /* @@ -257,7 +241,6 @@ void *newObj; void **Free; chunk = pool->Chunks; - if (pool->nextbest) /* start looking from chunk known to have frees */ chunk = pool->nextbest; @@ -266,30 +249,8 @@ pool->nextbest = chunk; assert(chunk->freeList); Free = chunk->freeList; -#if PARANOID - if (*Free) { - bounds = (void *) *Free - chunk->objCache; - assert(*Free && (bounds >= 0) && (bounds < pool->chunk_size)); - } -#endif chunk->freeList = *Free; -#if MEM_PROTECT - if ((pool->obj_size % MEM_PAGE_SIZE) == 0) { - if ( ( (int)Free % MEM_PAGE_SIZE) == 0) { - mprotect((void *) Free, pool->obj_size, PROT_READ | PROT_WRITE | PROT_EXEC); - } - } -#endif *Free = NULL; -#if PARANOID - bounds = (void *) Free - chunk->objCache; - assert(Free && (bounds >= 0) && (bounds < pool->chunk_size)); - if (chunk->freeList) { - bounds = chunk->freeList - chunk->objCache; - assert(chunk->freeList && (bounds >= 0) - && (bounds < pool->chunk_size)); - } -#endif chunk->count++; chunk->lastref = squid_curtime; return Free; @@ -314,14 +275,12 @@ pool->Chunks = new; return newObj; } - if (new->objCache < chunk->objCache) { /* we are lowest ram chunk, insert as first chunk */ new->next = chunk; chunk->prev = new; pool->Chunks = new; return newObj; } - while (chunk->next) { if (new->objCache < chunk->next->objCache) { /* new chunk is in lower ram, insert here */ new->next = chunk->next; @@ -433,7 +392,7 @@ mem_pool_free_calls++; #if XMALLOC_DEBUG2 if (obj != NULL) - check_free(obj); + check_free(obj); #endif memPoolPush(pool, obj); assert(pool->meter.idle.level <= pool->meter.alloc.level); @@ -557,7 +516,7 @@ a->capacity += delta; a->items = a->items ? xrealloc(a->items, a->capacity * sizeof(void *)) : - xmalloc(a->capacity * sizeof(void *)); + xmalloc(a->capacity * sizeof(void *)); /* reset, just in case */ memset(a->items + a->count, 0, (a->capacity - a->count) * sizeof(void *)); } Index: squid/lib/splay.c =================================================================== RCS file: /cvsroot/squid-sf//squid/lib/splay.c,v retrieving revision 1.3 retrieving revision 1.3.48.1 diff -u -r1.3 -r1.3.48.1 --- squid/lib/splay.c 23 Oct 2000 15:04:19 -0000 1.3 +++ squid/lib/splay.c 18 Apr 2001 18:17:43 -0000 1.3.48.1 @@ -1,5 +1,8 @@ /* - * $Id: splay.c,v 1.3 2000/10/23 15:04:19 hno Exp $ + * $Id: splay.c,v 1.3.48.1 2001/04/18 18:17:43 akroonmaa Exp $ + * + * based on ftp://ftp.cs.cmu.edu/user/sleator/splaying/top-down-splay.c + * http://bobo.link.cs.cmu.edu/cgi-bin/splay/splay-cgi.pl */ #include "config.h" @@ -99,6 +102,26 @@ return top; } +splayNode * +splay_delete(const void *data, splayNode * top, SPLAYCMP * compare) +{ + splayNode *x; + if (top == NULL) + return NULL; + top = splay_splay(data, top, compare); + if (splayLastResult == 0) { /* found it */ + if (top->left == NULL) { + x = top->right; + } else { + x = splay_splay(data, top->left, compare); + x->right = top->right; + } + xfree(top); + return x; + } + return top; /* It wasn't there */ +} + void splay_destroy(splayNode * top, SPLAYFREE * free_func) { Index: squid/src/MemPoolStats.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Attic/MemPoolStats.c,v retrieving revision 1.1.2.4 retrieving revision 1.1.2.5 diff -u -r1.1.2.4 -r1.1.2.5 --- squid/src/MemPoolStats.c 11 Apr 2001 09:35:39 -0000 1.1.2.4 +++ squid/src/MemPoolStats.c 18 Apr 2001 18:17:43 -0000 1.1.2.5 @@ -170,6 +170,7 @@ static void memPoolMeterReport(const MemPoolMeter * pm, size_t obj_size, +//memPoolMeterReport( MemPoolMeter * pm, size_t obj_size, int alloc_count, int inuse_count, int idle_count, StoreEntry * e) { assert(pm); Index: squid/src/stat.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/stat.c,v retrieving revision 1.9 retrieving revision 1.9.8.1 diff -u -r1.9 -r1.9.8.1 --- squid/src/stat.c 3 Mar 2001 10:44:32 -0000 1.9 +++ squid/src/stat.c 18 Apr 2001 18:17:43 -0000 1.9.8.1 @@ -1,6 +1,6 @@ /* - * $Id: stat.c,v 1.9 2001/03/03 10:44:32 squidadm Exp $ + * $Id: stat.c,v 1.9.8.1 2001/04/18 18:17:43 akroonmaa Exp $ * * DEBUG: section 18 Cache Manager Statistics * AUTHOR: Harvest Derived @@ -577,6 +577,9 @@ t = mp.fsmblks + mp.fordblks; storeAppendPrintf(sentry, "\tTotal free: %6d KB %d%%\n", t >> 10, percent(t, mp.arena)); + t = mp.uordblks + mp.usmblks + mp.hblkhd + mp.fsmblks + mp.fordblks; + storeAppendPrintf(sentry, "\tTotal holding: %6d KB\n", + t >> 10); #if HAVE_EXT_MALLINFO storeAppendPrintf(sentry, "\tmax size of small blocks:\t%d\n", mp.mxfast); storeAppendPrintf(sentry, "\tnumber of small blocks in a holding block:\t%d\n", @@ -593,6 +596,8 @@ storeAppendPrintf(sentry, "Memory accounted for:\n"); storeAppendPrintf(sentry, "\tTotal accounted: %6d KB\n", statMemoryAccounted() >> 10); + storeAppendPrintf(sentry, "\tmemPool accounted: %6d KB\n", + memTotalAllocated() >> 10); storeAppendPrintf(sentry, "\tmemPoolAlloc calls: %d\n", mem_pool_alloc_calls); storeAppendPrintf(sentry, "\tmemPoolFree calls: %d\n",