# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: henrik@henriknordstrom.net-20100528215644-\ # wyillk99j8xh6gsz # target_branch: http://www.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 2620126df44429193c1456740f28dc63960ed438 # timestamp: 2010-05-28 23:58:31 +0200 # base_revision_id: henrik@henriknordstrom.net-20100528215627-\ # agkq7l8rn7lertwg # # Begin patch === modified file 'include/MemPool.h' --- include/MemPool.h 2010-03-21 03:08:26 +0000 +++ include/MemPool.h 2010-05-28 21:53:06 +0000 @@ -1,5 +1,5 @@ -#ifndef _MEM_POOLS_H_ -#define _MEM_POOLS_H_ +#ifndef _MEM_POOL_H_ +#define _MEM_POOL_H_ /** \defgroup MemPoolsAPI Memory Management (Memory Pool Allocator) @@ -63,7 +63,6 @@ #define MEM_MAX_FREE 65535 /* ushort is max number of items per chunk */ class MemImplementingAllocator; -class MemChunk; class MemPoolStats; /// \ingroup MemPoolsAPI @@ -130,13 +129,6 @@ MemImplementingAllocator * create(const char *label, size_t obj_size); /** - \param label Name for the pool. Displayed in stats. - \param obj_size Size of elements in MemPool. - \param chunked ?? - */ - MemImplementingAllocator * create(const char *label, size_t obj_size, bool const chunked); - - /** * Sets upper limit in bytes to amount of free ram kept in pools. This is * not strict upper limit, but a hint. When MemPools are over this limit, * totally free chunks are immediately considered for release. Otherwise @@ -197,7 +189,7 @@ \param stats Object to be filled with statistical data about pool. \retval Number of objects in use, ie. allocated. */ - virtual int getStats(MemPoolStats * stats) = 0; + virtual int getStats(MemPoolStats * stats, int accumulate = 0) = 0; virtual MemPoolMeter const &getMeter() const = 0; @@ -342,15 +334,13 @@ virtual bool idleTrigger(int shift) const = 0; virtual void clean(time_t maxage) = 0; - /** Hint to the allocator - may be ignored */ - virtual void setChunkSize(size_t chunksize) {} virtual size_t objectSize() const; virtual int getInUseCount() = 0; protected: virtual void *allocate() = 0; virtual void deallocate(void *) = 0; -private: MemPoolMeter meter; + int memPID; public: MemImplementingAllocator *next; public: @@ -360,93 +350,6 @@ }; /// \ingroup MemPoolsAPI -class MemPool : public MemImplementingAllocator -{ -public: - friend class MemChunk; - MemPool(const char *label, size_t obj_size); - ~MemPool(); - void convertFreeCacheToChunkFreeCache(); - virtual void clean(time_t maxage); - - /** - \param stats Object to be filled with statistical data about pool. - \retval Number of objects in use, ie. allocated. - */ - virtual int getStats(MemPoolStats * stats); - - void createChunk(); - void *get(); - void push(void *obj); - virtual int getInUseCount(); -protected: - virtual void *allocate(); - virtual void deallocate(void *); -public: - /** - * Allows you tune chunk size of pooling. Objects are allocated in chunks - * instead of individually. This conserves memory, reduces fragmentation. - * Because of that memory can be freed also only in chunks. Therefore - * there is tradeoff between memory conservation due to chunking and free - * memory fragmentation. - * - \note As a general guideline, increase chunk size only for pools that keep - * very many items for relatively long time. - */ - virtual void setChunkSize(size_t chunksize); - - virtual bool idleTrigger(int shift) const; - - size_t chunk_size; - int chunk_capacity; - int memPID; - int chunkCount; - size_t inuse; - size_t idle; - void *freeCache; - MemChunk *nextFreeChunk; - MemChunk *Chunks; - Splay allChunks; -}; - -/// \ingroup MemPoolsAPI -class MemMalloc : public MemImplementingAllocator -{ -public: - MemMalloc(char const *label, size_t aSize); - virtual bool idleTrigger(int shift) const; - virtual void clean(time_t maxage); - - /** - \param stats Object to be filled with statistical data about pool. - \retval Number of objects in use, ie. allocated. - */ - virtual int getStats(MemPoolStats * stats); - - virtual int getInUseCount(); -protected: - virtual void *allocate(); - virtual void deallocate(void *); -private: - int inuse; -}; - -/// \ingroup MemPoolsAPI -class MemChunk -{ -public: - MemChunk(MemPool *pool); - ~MemChunk(); - void *freeList; - void *objCache; - int inuse_count; - MemChunk *nextFreeChunk; - MemChunk *next; - time_t lastref; - MemPool *pool; -}; - -/// \ingroup MemPoolsAPI class MemPoolStats { public: @@ -536,4 +439,4 @@ } -#endif /* _MEM_POOLS_H_ */ +#endif /* _MEM_POOL_H_ */ === added file 'include/MemPoolChunked.h' --- include/MemPoolChunked.h 1970-01-01 00:00:00 +0000 +++ include/MemPoolChunked.h 2010-05-28 21:53:06 +0000 @@ -0,0 +1,82 @@ +#ifndef _MEM_POOL_CHUNKED_H_ +#define _MEM_POOL_CHUNKED_H_ + +#include "MemPool.h" + +/// \ingroup MemPoolsAPI +#define MEM_PAGE_SIZE 4096 +/// \ingroup MemPoolsAPI +#define MEM_CHUNK_SIZE 4096 * 4 +/// \ingroup MemPoolsAPI +#define MEM_CHUNK_MAX_SIZE 256 * 1024 /* 2MB */ +/// \ingroup MemPoolsAPI +#define MEM_MIN_FREE 32 +/// \ingroup MemPoolsAPI +#define MEM_MAX_FREE 65535 /* ushort is max number of items per chunk */ + +class MemChunk; + +/// \ingroup MemPoolsAPI +class MemPoolChunked : public MemImplementingAllocator +{ +public: + friend class MemChunk; + MemPoolChunked(const char *label, size_t obj_size); + ~MemPoolChunked(); + void convertFreeCacheToChunkFreeCache(); + virtual void clean(time_t maxage); + + /** + \param stats Object to be filled with statistical data about pool. + \retval Number of objects in use, ie. allocated. + */ + virtual int getStats(MemPoolStats * stats, int accumulate); + + void createChunk(); + void *get(); + void push(void *obj); + virtual int getInUseCount(); +protected: + virtual void *allocate(); + virtual void deallocate(void *); +public: + /** + * Allows you tune chunk size of pooling. Objects are allocated in chunks + * instead of individually. This conserves memory, reduces fragmentation. + * Because of that memory can be freed also only in chunks. Therefore + * there is tradeoff between memory conservation due to chunking and free + * memory fragmentation. + * + \note As a general guideline, increase chunk size only for pools that keep + * very many items for relatively long time. + */ + virtual void setChunkSize(size_t chunksize); + + virtual bool idleTrigger(int shift) const; + + size_t chunk_size; + int chunk_capacity; + int memPID; + int chunkCount; + void *freeCache; + MemChunk *nextFreeChunk; + MemChunk *Chunks; + Splay allChunks; +}; + +/// \ingroup MemPoolsAPI +class MemChunk +{ +public: + MemChunk(MemPoolChunked *pool); + ~MemChunk(); + void *freeList; + void *objCache; + int inuse_count; + MemChunk *nextFreeChunk; + MemChunk *next; + time_t lastref; + MemPoolChunked *pool; +}; + +#endif /* _MEM_POOL_CHUNKED_H_ */ === added file 'include/MemPoolMalloc.h' --- include/MemPoolMalloc.h 1970-01-01 00:00:00 +0000 +++ include/MemPoolMalloc.h 2010-05-28 21:53:06 +0000 @@ -0,0 +1,47 @@ +#ifndef _MEM_POOL_MALLOC_H_ +#define _MEM_POOL_MALLOC_H_ + +/** + \defgroup MemPoolsAPI Memory Management (Memory Pool Allocator) + \ingroup Components + * + *\par + * MemPools are a pooled memory allocator running on top of malloc(). It's + * purpose is to reduce memory fragmentation and provide detailed statistics + * on memory consumption. + * + \par + * Preferably all memory allocations in Squid should be done using MemPools + * or one of the types built on top of it (i.e. cbdata). + * + \note Usually it is better to use cbdata types as these gives you additional + * safeguards in references and typechecking. However, for high usage pools where + * the cbdata functionality of cbdata is not required directly using a MemPool + * might be the way to go. + */ + +#include "MemPool.h" + +/// \ingroup MemPoolsAPI +class MemPoolMalloc : public MemImplementingAllocator +{ +public: + MemPoolMalloc(char const *label, size_t aSize); + virtual bool idleTrigger(int shift) const; + virtual void clean(time_t maxage); + + /** + \param stats Object to be filled with statistical data about pool. + \retval Number of objects in use, ie. allocated. + */ + virtual int getStats(MemPoolStats * stats, int accumulate); + + virtual int getInUseCount(); +protected: + virtual void *allocate(); + virtual void deallocate(void *); +private: +}; + + +#endif /* _MEM_POOL_MALLOC_H_ */ === modified file 'lib/Makefile.am' --- lib/Makefile.am 2009-12-19 11:56:02 +0000 +++ lib/Makefile.am 2010-05-28 21:53:06 +0000 @@ -49,6 +49,8 @@ win32lib.c libmiscutil_a_SOURCES = \ MemPool.cc \ + MemPoolChunked.cc \ + MemPoolMalloc.cc \ base64.c \ charset.c \ getfullhostname.c \ === modified file 'lib/MemPool.cc' --- lib/MemPool.cc 2010-04-14 21:04:28 +0000 +++ lib/MemPool.cc 2010-05-28 21:53:06 +0000 @@ -87,6 +87,8 @@ #endif #include "MemPool.h" +#include "MemPoolChunked.h" +#include "MemPoolMalloc.h" #define FLUSH_LIMIT 1000 /* Flush memPool counters to memMeters after flush limit calls */ #define MEM_MAX_MMAP_CHUNKS 2048 @@ -107,10 +109,6 @@ static int Pool_id_counter = 0; -/* local prototypes */ -static int memCompChunks(MemChunk * const &, MemChunk * const &); -static int memCompObjChunks(void * const &, MemChunk * const &); - MemPools & MemPools::GetInstance() { @@ -165,195 +163,6 @@ return mem_idle_limit; } -/* Compare chunks */ -static int -memCompChunks(MemChunk * const &chunkA, MemChunk * const &chunkB) -{ - if (chunkA->objCache > chunkB->objCache) - return 1; - else if (chunkA->objCache < chunkB->objCache) - return -1; - else - return 0; -} - -/* Compare object to chunk */ -static int -memCompObjChunks(void *const &obj, MemChunk * const &chunk) -{ - /* object is lower in memory than the chunks arena */ - if (obj < chunk->objCache) - return -1; - /* object is within the pool */ - if (obj < (void *) ((char *) chunk->objCache + chunk->pool->chunk_size)) - return 0; - /* object is above the pool */ - return 1; -} - -MemChunk::MemChunk(MemPool *aPool) -{ - /* should have a pool for this too - - * note that this requres: - * allocate one chunk for the pool of chunks's first chunk - * allocate a chunk from that pool - * move the contents of one chunk into the other - * free the first chunk. - */ - inuse_count = 0; - next = NULL; - pool = aPool; - - objCache = xcalloc(1, pool->chunk_size); - freeList = objCache; - void **Free = (void **)freeList; - - for (int i = 1; i < pool->chunk_capacity; i++) { - *Free = (void *) ((char *) Free + pool->obj_size); - void **nextFree = (void **)*Free; - (void) VALGRIND_MAKE_MEM_NOACCESS(Free, pool->obj_size); - Free = nextFree; - } - nextFreeChunk = pool->nextFreeChunk; - pool->nextFreeChunk = this; - - memMeterAdd(pool->getMeter().alloc, pool->chunk_capacity); - memMeterAdd(pool->getMeter().idle, pool->chunk_capacity); - pool->idle += pool->chunk_capacity; - pool->chunkCount++; - lastref = squid_curtime; - pool->allChunks.insert(this, memCompChunks); -} - -MemPool::MemPool(const char *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize) -{ - chunk_size = 0; - chunk_capacity = 0; - memPID = 0; - chunkCount = 0; - inuse = 0; - idle = 0; - freeCache = 0; - nextFreeChunk = 0; - Chunks = 0; - next = 0; - MemImplementingAllocator *last_pool; - - assert(aLabel != NULL && aSize); - - setChunkSize(MEM_CHUNK_SIZE); - - /* Append as Last */ - for (last_pool = MemPools::GetInstance().pools; last_pool && last_pool->next;) - last_pool = last_pool->next; - if (last_pool) - last_pool->next = this; - else - MemPools::GetInstance().pools = this; - - memPID = ++Pool_id_counter; -} - -MemChunk::~MemChunk() -{ - memMeterDel(pool->getMeter().alloc, pool->chunk_capacity); - memMeterDel(pool->getMeter().idle, pool->chunk_capacity); - pool->idle -= pool->chunk_capacity; - pool->chunkCount--; - pool->allChunks.remove(this, memCompChunks); - xfree(objCache); -} - -void -MemPool::push(void *obj) -{ - void **Free; - /* XXX We should figure out a sane way of avoiding having to clear - * all buffers. For example data buffers such as used by MemBuf do - * not really need to be cleared.. There was a condition based on - * the object size here, but such condition is not safe. - */ - if (doZeroOnPush) - memset(obj, 0, obj_size); - Free = (void **)obj; - *Free = freeCache; - freeCache = obj; - (void) VALGRIND_MAKE_MEM_NOACCESS(obj, obj_size); -} - -/* - * Find a chunk with a free item. - * Create new chunk on demand if no chunk with frees found. - * Insert new chunk in front of lowest ram chunk, making it preferred in future, - * and resulting slow compaction towards lowest ram area. - */ -void * -MemPool::get() -{ - void **Free; - - /* first, try cache */ - if (freeCache) { - Free = (void **)freeCache; - (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size); - freeCache = *Free; - *Free = NULL; - return Free; - } - /* then try perchunk freelist chain */ - if (nextFreeChunk == NULL) { - /* no chunk with frees, so create new one */ - createChunk(); - } - /* now we have some in perchunk freelist chain */ - MemChunk *chunk = nextFreeChunk; - - Free = (void **)chunk->freeList; - chunk->freeList = *Free; - *Free = NULL; - chunk->inuse_count++; - chunk->lastref = squid_curtime; - - if (chunk->freeList == NULL) { - /* last free in this chunk, so remove us from perchunk freelist chain */ - nextFreeChunk = chunk->nextFreeChunk; - } - (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size); - return Free; -} - -/* just create a new chunk and place it into a good spot in the chunk chain */ -void -MemPool::createChunk() -{ - MemChunk *chunk, *newChunk; - - newChunk = new MemChunk(this); - - chunk = Chunks; - if (chunk == NULL) { /* first chunk in pool */ - Chunks = newChunk; - return; - } - if (newChunk->objCache < chunk->objCache) { - /* we are lowest ram chunk, insert as first chunk */ - newChunk->next = chunk; - Chunks = newChunk; - return; - } - while (chunk->next) { - if (newChunk->objCache < chunk->next->objCache) { - /* new chunk is in lower ram, insert here */ - newChunk->next = chunk->next; - chunk->next = newChunk; - return; - } - chunk = chunk->next; - } - /* we are the worst chunk in chain, add as last */ - chunk->next = newChunk; -} - /* Change the default calue of defaultIsChunked to override * all pools - including those used before main() starts where * MemPools::GetInstance().setDefaultPoolChunking() can be called. @@ -369,49 +178,14 @@ #endif } -void -MemPool::setChunkSize(size_t chunksize) -{ - int cap; - size_t csize = chunksize; - - if (Chunks) /* unsafe to tamper */ - return; - - csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE; /* round up to page size */ - cap = csize / obj_size; - - if (cap < MEM_MIN_FREE) - cap = MEM_MIN_FREE; - if (cap * obj_size > MEM_CHUNK_MAX_SIZE) - cap = MEM_CHUNK_MAX_SIZE / obj_size; - if (cap > MEM_MAX_FREE) - cap = MEM_MAX_FREE; - if (cap < 1) - cap = 1; - - csize = cap * obj_size; - csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE; /* round up to page size */ - cap = csize / obj_size; - - chunk_capacity = cap; - chunk_size = csize; -} - MemImplementingAllocator * MemPools::create(const char *label, size_t obj_size) { - return create (label, obj_size, defaultIsChunked); -} - -MemImplementingAllocator * -MemPools::create(const char *label, size_t obj_size, bool const chunked) -{ ++poolCount; - if (chunked) - return new MemPool (label, obj_size); + if (defaultIsChunked) + return new MemPoolChunked (label, obj_size); else - return new MemMalloc (label, obj_size); + return new MemPoolMalloc (label, obj_size); } void @@ -420,40 +194,6 @@ defaultIsChunked = aBool; } -/* - * warning: we do not clean this entry from Pools assuming destruction - * is used at the end of the program only - */ -MemPool::~MemPool() -{ - MemChunk *chunk, *fchunk; - MemImplementingAllocator *find_pool, *prev_pool; - - flushMetersFull(); - clean(0); - assert(inuse == 0 && "While trying to destroy pool"); - - chunk = Chunks; - while ( (fchunk = chunk) != NULL) { - chunk = chunk->next; - delete fchunk; - } - /* TODO we should be doing something about the original Chunks pointer here. */ - - assert(MemPools::GetInstance().pools != NULL && "Called MemPool::~MemPool, but no pool exists!"); - - /* Pool clean, remove it from List and free */ - for (find_pool = MemPools::GetInstance().pools, prev_pool = NULL; (find_pool && this != find_pool); find_pool = find_pool->next) - prev_pool = find_pool; - assert(find_pool != NULL && "pool to destroy not found"); - - if (prev_pool) - prev_pool->next = next; - else - MemPools::GetInstance().pools = next; - --MemPools::GetInstance().poolCount; -} - char const * MemAllocator::objectType() const { @@ -473,16 +213,12 @@ calls = free_calls; if (calls) { - getMeter().gb_freed.count += calls; - memMeterDel(getMeter().inuse, calls); - memMeterAdd(getMeter().idle, calls); + meter.gb_freed.count += calls; free_calls = 0; } calls = alloc_calls; if (calls) { meter.gb_saved.count += calls; - memMeterAdd(meter.inuse, calls); - memMeterDel(meter.idle, calls); alloc_calls = 0; } } @@ -532,20 +268,6 @@ } void * -MemMalloc::allocate() -{ - inuse++; - return xcalloc(1, obj_size); -} - -void -MemMalloc::deallocate(void *obj) -{ - inuse--; - xfree(obj); -} - -void * MemImplementingAllocator::alloc() { if (++alloc_calls == FLUSH_LIMIT) @@ -563,123 +285,6 @@ ++free_calls; } -int -MemPool::getInUseCount() -{ - return inuse; -} - -void * -MemPool::allocate() -{ - void *p = get(); - assert(idle); - --idle; - ++inuse; - return p; -} - -void -MemPool::deallocate(void *obj) -{ - push(obj); - assert(inuse); - --inuse; - ++idle; -} - -void -MemPool::convertFreeCacheToChunkFreeCache() -{ - void *Free; - /* - * OK, so we have to go through all the global freeCache and find the Chunk - * any given Free belongs to, and stuff it into that Chunk's freelist - */ - - while ((Free = freeCache) != NULL) { - MemChunk *chunk = NULL; - chunk = const_cast(*allChunks.find(Free, memCompObjChunks)); - assert(splayLastResult == 0); - assert(chunk->inuse_count > 0); - chunk->inuse_count--; - (void) VALGRIND_MAKE_MEM_DEFINED(Free, sizeof(void *)); - freeCache = *(void **)Free; /* remove from global cache */ - *(void **)Free = chunk->freeList; /* stuff into chunks freelist */ - (void) VALGRIND_MAKE_MEM_NOACCESS(Free, sizeof(void *)); - chunk->freeList = Free; - chunk->lastref = squid_curtime; - } - -} - -/* removes empty Chunks from pool */ -void -MemPool::clean(time_t maxage) -{ - MemChunk *chunk, *freechunk, *listTail; - time_t age; - - if (!this) - return; - if (!Chunks) - return; - - flushMetersFull(); - convertFreeCacheToChunkFreeCache(); - /* Now we have all chunks in this pool cleared up, all free items returned to their home */ - /* We start now checking all chunks to see if we can release any */ - /* We start from Chunks->next, so first chunk is not released */ - /* Recreate nextFreeChunk list from scratch */ - - chunk = Chunks; - while ((freechunk = chunk->next) != NULL) { - age = squid_curtime - freechunk->lastref; - freechunk->nextFreeChunk = NULL; - if (freechunk->inuse_count == 0) - if (age >= maxage) { - chunk->next = freechunk->next; - delete freechunk; - freechunk = NULL; - } - if (chunk->next == NULL) - break; - chunk = chunk->next; - } - - /* Recreate nextFreeChunk list from scratch */ - /* Populate nextFreeChunk list in order of "most filled chunk first" */ - /* in case of equal fill, put chunk in lower ram first */ - /* First (create time) chunk is always on top, no matter how full */ - - chunk = Chunks; - nextFreeChunk = chunk; - chunk->nextFreeChunk = NULL; - - while (chunk->next) { - chunk->next->nextFreeChunk = NULL; - if (chunk->next->inuse_count < chunk_capacity) { - listTail = nextFreeChunk; - while (listTail->nextFreeChunk) { - if (chunk->next->inuse_count > listTail->nextFreeChunk->inuse_count) - break; - if ((chunk->next->inuse_count == listTail->nextFreeChunk->inuse_count) && - (chunk->next->objCache < listTail->nextFreeChunk->objCache)) - break; - listTail = listTail->nextFreeChunk; - } - chunk->next->nextFreeChunk = listTail->nextFreeChunk; - listTail->nextFreeChunk = chunk->next; - } - chunk = chunk->next; - } - /* We started from 2nd chunk. If first chunk is full, remove it */ - if (nextFreeChunk->inuse_count == chunk_capacity) - nextFreeChunk = nextFreeChunk->nextFreeChunk; - - return; -} - /* * Returns all cached frees to their home chunks * If chunks unreferenced age is over, destroys Idle chunk @@ -707,96 +312,10 @@ memPoolIterateDone(&iter); } -bool -MemPool::idleTrigger(int shift) const -{ - return getMeter().idle.level > (chunk_capacity << shift); -} - /* Persistent Pool stats. for GlobalStats accumulation */ static MemPoolStats pp_stats; /* - * Update MemPoolStats struct for single pool - */ -int -MemPool::getStats(MemPoolStats * stats) -{ - MemChunk *chunk; - int chunks_free = 0; - int chunks_partial = 0; - - if (stats != &pp_stats) /* need skip memset for GlobalStats accumulation */ - /* XXX Fixme */ - memset(stats, 0, sizeof(MemPoolStats)); - - clean((time_t) 555555); /* don't want to get chunks released before reporting */ - - stats->pool = this; - stats->label = objectType(); - stats->meter = &getMeter(); - stats->obj_size = obj_size; - stats->chunk_capacity = chunk_capacity; - - /* gather stats for each Chunk */ - chunk = Chunks; - while (chunk) { - if (chunk->inuse_count == 0) - chunks_free++; - else if (chunk->inuse_count < chunk_capacity) - chunks_partial++; - chunk = chunk->next; - } - - stats->chunks_alloc += chunkCount; - stats->chunks_inuse += chunkCount - chunks_free; - stats->chunks_partial += chunks_partial; - stats->chunks_free += chunks_free; - - stats->items_alloc += getMeter().alloc.level; - stats->items_inuse += getMeter().inuse.level; - stats->items_idle += getMeter().idle.level; - - stats->overhead += sizeof(MemPool) + chunkCount * sizeof(MemChunk) + strlen(objectType()) + 1; - - return getMeter().inuse.level; -} - -/* TODO extract common logic to MemAllocate */ -int -MemMalloc::getStats(MemPoolStats * stats) -{ - if (stats != &pp_stats) /* need skip memset for GlobalStats accumulation */ - /* XXX Fixme */ - memset(stats, 0, sizeof(MemPoolStats)); - - stats->pool = this; - stats->label = objectType(); - stats->meter = &getMeter(); - stats->obj_size = obj_size; - stats->chunk_capacity = 0; - - stats->chunks_alloc += 0; - stats->chunks_inuse += 0; - stats->chunks_partial += 0; - stats->chunks_free += 0; - - stats->items_alloc += getMeter().alloc.level; - stats->items_inuse += getMeter().inuse.level; - stats->items_idle += getMeter().idle.level; - - stats->overhead += sizeof(MemMalloc) + strlen(objectType()) + 1; - - return getMeter().inuse.level; -} - -int -MemMalloc::getInUseCount() -{ - return inuse; -} - -/* * Totals statistics is returned */ int @@ -814,7 +333,7 @@ /* gather all stats for Totals */ iter = memPoolIterate(); while ((pool = memPoolIterateNext(iter))) { - if (pool->getStats(&pp_stats) > 0) + if (pool->getStats(&pp_stats, 1) > 0) pools_inuse++; } memPoolIterateDone(&iter); @@ -833,7 +352,7 @@ stats->tot_items_inuse = pp_stats.items_inuse; stats->tot_items_idle = pp_stats.items_idle; - stats->tot_overhead += pp_stats.overhead + MemPools::GetInstance().poolCount * sizeof(MemPool *); + stats->tot_overhead += pp_stats.overhead + MemPools::GetInstance().poolCount * sizeof(MemAllocator *); stats->mem_idle_limit = MemPools::GetInstance().mem_idle_limit; return pools_inuse; @@ -848,19 +367,6 @@ return ((s + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*); } -MemMalloc::MemMalloc(char const *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize) { inuse = 0; } - -bool -MemMalloc::idleTrigger(int shift) const -{ - return false; -} - -void -MemMalloc::clean(time_t maxage) -{ -} - int memPoolInUseCount(MemAllocator * pool) { @@ -937,6 +443,7 @@ free_calls(0), obj_size(RoundedSize(aSize)) { + memPID = ++Pool_id_counter; } void === added file 'lib/MemPoolChunked.cc' --- lib/MemPoolChunked.cc 1970-01-01 00:00:00 +0000 +++ lib/MemPoolChunked.cc 2010-05-28 21:53:06 +0000 @@ -0,0 +1,519 @@ + +/* + * $Id$ + * + * DEBUG: section 63 Low Level Memory Pool Management + * AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * the Regents of the University of California. Please see the + * COPYRIGHT file for full details. Squid incorporates software + * developed and/or copyrighted by other sources. Please see the + * CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +/* + * 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.] (not implemented) + * + * In cachemgr output, there are new columns for chunking. Special item, + * Frag, 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. Frag 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. + * "part" item shows number of chunks partially filled. This shows how + * badly fragmentation is spread across all chunks. + * + * Andres Kroonmaa. + * Copyright (c) 2003, Robert Collins + */ + +#include "config.h" +#if HAVE_ASSERT_H +#include +#endif + +#include "MemPoolChunked.h" + +#define FLUSH_LIMIT 1000 /* Flush memPool counters to memMeters after flush limit calls */ +#define MEM_MAX_MMAP_CHUNKS 2048 + +#if HAVE_STRING_H +#include +#endif + +/* + * XXX This is a boundary violation between lib and src.. would be good + * if it could be solved otherwise, but left for now. + */ +extern time_t squid_curtime; + +/* local prototypes */ +static int memCompChunks(MemChunk * const &, MemChunk * const &); +static int memCompObjChunks(void * const &, MemChunk * const &); + +/* Compare chunks */ +static int +memCompChunks(MemChunk * const &chunkA, MemChunk * const &chunkB) +{ + if (chunkA->objCache > chunkB->objCache) + return 1; + else if (chunkA->objCache < chunkB->objCache) + return -1; + else + return 0; +} + +/* Compare object to chunk */ +static int +memCompObjChunks(void *const &obj, MemChunk * const &chunk) +{ + /* object is lower in memory than the chunks arena */ + if (obj < chunk->objCache) + return -1; + /* object is within the pool */ + if (obj < (void *) ((char *) chunk->objCache + chunk->pool->chunk_size)) + return 0; + /* object is above the pool */ + return 1; +} + +MemChunk::MemChunk(MemPoolChunked *aPool) +{ + /* should have a pool for this too - + * note that this requres: + * allocate one chunk for the pool of chunks's first chunk + * allocate a chunk from that pool + * move the contents of one chunk into the other + * free the first chunk. + */ + inuse_count = 0; + next = NULL; + pool = aPool; + + objCache = xcalloc(1, pool->chunk_size); + freeList = objCache; + void **Free = (void **)freeList; + + for (int i = 1; i < pool->chunk_capacity; i++) { + *Free = (void *) ((char *) Free + pool->obj_size); + void **nextFree = (void **)*Free; + (void) VALGRIND_MAKE_MEM_NOACCESS(Free, pool->obj_size); + Free = nextFree; + } + nextFreeChunk = pool->nextFreeChunk; + pool->nextFreeChunk = this; + + memMeterAdd(pool->getMeter().alloc, pool->chunk_capacity); + memMeterAdd(pool->getMeter().idle, pool->chunk_capacity); + pool->chunkCount++; + lastref = squid_curtime; + pool->allChunks.insert(this, memCompChunks); +} + +MemPoolChunked::MemPoolChunked(const char *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize) +{ + chunk_size = 0; + chunk_capacity = 0; + chunkCount = 0; + freeCache = 0; + nextFreeChunk = 0; + Chunks = 0; + next = 0; + MemImplementingAllocator *last_pool; + + assert(aLabel != NULL && aSize); + + setChunkSize(MEM_CHUNK_SIZE); + + /* Append as Last */ + for (last_pool = MemPools::GetInstance().pools; last_pool && last_pool->next;) + last_pool = last_pool->next; + if (last_pool) + last_pool->next = this; + else + MemPools::GetInstance().pools = this; +} + +MemChunk::~MemChunk() +{ + memMeterDel(pool->getMeter().alloc, pool->chunk_capacity); + memMeterDel(pool->getMeter().idle, pool->chunk_capacity); + pool->chunkCount--; + pool->allChunks.remove(this, memCompChunks); + xfree(objCache); +} + +void +MemPoolChunked::push(void *obj) +{ + void **Free; + /* XXX We should figure out a sane way of avoiding having to clear + * all buffers. For example data buffers such as used by MemBuf do + * not really need to be cleared.. There was a condition based on + * the object size here, but such condition is not safe. + */ + if (doZeroOnPush) + memset(obj, 0, obj_size); + Free = (void **)obj; + *Free = freeCache; + freeCache = obj; + (void) VALGRIND_MAKE_MEM_NOACCESS(obj, obj_size); +} + +/* + * Find a chunk with a free item. + * Create new chunk on demand if no chunk with frees found. + * Insert new chunk in front of lowest ram chunk, making it preferred in future, + * and resulting slow compaction towards lowest ram area. + */ +void * +MemPoolChunked::get() +{ + void **Free; + + /* first, try cache */ + if (freeCache) { + Free = (void **)freeCache; + (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size); + freeCache = *Free; + *Free = NULL; + return Free; + } + /* then try perchunk freelist chain */ + if (nextFreeChunk == NULL) { + /* no chunk with frees, so create new one */ + createChunk(); + } + /* now we have some in perchunk freelist chain */ + MemChunk *chunk = nextFreeChunk; + + Free = (void **)chunk->freeList; + chunk->freeList = *Free; + *Free = NULL; + chunk->inuse_count++; + chunk->lastref = squid_curtime; + + if (chunk->freeList == NULL) { + /* last free in this chunk, so remove us from perchunk freelist chain */ + nextFreeChunk = chunk->nextFreeChunk; + } + (void) VALGRIND_MAKE_MEM_DEFINED(Free, obj_size); + return Free; +} + +/* just create a new chunk and place it into a good spot in the chunk chain */ +void +MemPoolChunked::createChunk() +{ + MemChunk *chunk, *newChunk; + + newChunk = new MemChunk(this); + + chunk = Chunks; + if (chunk == NULL) { /* first chunk in pool */ + Chunks = newChunk; + return; + } + if (newChunk->objCache < chunk->objCache) { + /* we are lowest ram chunk, insert as first chunk */ + newChunk->next = chunk; + Chunks = newChunk; + return; + } + while (chunk->next) { + if (newChunk->objCache < chunk->next->objCache) { + /* new chunk is in lower ram, insert here */ + newChunk->next = chunk->next; + chunk->next = newChunk; + return; + } + chunk = chunk->next; + } + /* we are the worst chunk in chain, add as last */ + chunk->next = newChunk; +} + +void +MemPoolChunked::setChunkSize(size_t chunksize) +{ + int cap; + size_t csize = chunksize; + + if (Chunks) /* unsafe to tamper */ + return; + + csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE; /* round up to page size */ + cap = csize / obj_size; + + if (cap < MEM_MIN_FREE) + cap = MEM_MIN_FREE; + if (cap * obj_size > MEM_CHUNK_MAX_SIZE) + cap = MEM_CHUNK_MAX_SIZE / obj_size; + if (cap > MEM_MAX_FREE) + cap = MEM_MAX_FREE; + if (cap < 1) + cap = 1; + + csize = cap * obj_size; + csize = ((csize + MEM_PAGE_SIZE - 1) / MEM_PAGE_SIZE) * MEM_PAGE_SIZE; /* round up to page size */ + cap = csize / obj_size; + + chunk_capacity = cap; + chunk_size = csize; +} + +/* + * warning: we do not clean this entry from Pools assuming destruction + * is used at the end of the program only + */ +MemPoolChunked::~MemPoolChunked() +{ + MemChunk *chunk, *fchunk; + MemImplementingAllocator *find_pool, *prev_pool; + + flushMetersFull(); + clean(0); + assert(getMeter().inuse.level == 0 && "While trying to destroy pool"); + + chunk = Chunks; + while ( (fchunk = chunk) != NULL) { + chunk = chunk->next; + delete fchunk; + } + /* TODO we should be doing something about the original Chunks pointer here. */ + + assert(MemPools::GetInstance().pools != NULL && "Called MemPoolChunked::~MemPoolChunked, but no pool exists!"); + + /* Pool clean, remove it from List and free */ + for (find_pool = MemPools::GetInstance().pools, prev_pool = NULL; (find_pool && this != find_pool); find_pool = find_pool->next) + prev_pool = find_pool; + assert(find_pool != NULL && "pool to destroy not found"); + + if (prev_pool) + prev_pool->next = next; + else + MemPools::GetInstance().pools = next; + --MemPools::GetInstance().poolCount; +} + +int +MemPoolChunked::getInUseCount() +{ + return getMeter().inuse.level; +} + +void * +MemPoolChunked::allocate() +{ + void *p = get(); + assert(meter.idle.level > 0); + memMeterDec(meter.idle); + memMeterInc(meter.inuse); + return p; +} + +void +MemPoolChunked::deallocate(void *obj) +{ + push(obj); + assert(meter.inuse.level > 0); + memMeterDec(meter.inuse); + memMeterInc(meter.idle); +} + +void +MemPoolChunked::convertFreeCacheToChunkFreeCache() +{ + void *Free; + /* + * OK, so we have to go through all the global freeCache and find the Chunk + * any given Free belongs to, and stuff it into that Chunk's freelist + */ + + while ((Free = freeCache) != NULL) { + MemChunk *chunk = NULL; + chunk = const_cast(*allChunks.find(Free, memCompObjChunks)); + assert(splayLastResult == 0); + assert(chunk->inuse_count > 0); + chunk->inuse_count--; + (void) VALGRIND_MAKE_MEM_DEFINED(Free, sizeof(void *)); + freeCache = *(void **)Free; /* remove from global cache */ + *(void **)Free = chunk->freeList; /* stuff into chunks freelist */ + (void) VALGRIND_MAKE_MEM_NOACCESS(Free, sizeof(void *)); + chunk->freeList = Free; + chunk->lastref = squid_curtime; + } + +} + +/* removes empty Chunks from pool */ +void +MemPoolChunked::clean(time_t maxage) +{ + MemChunk *chunk, *freechunk, *listTail; + time_t age; + + if (!this) + return; + if (!Chunks) + return; + + flushMetersFull(); + convertFreeCacheToChunkFreeCache(); + /* Now we have all chunks in this pool cleared up, all free items returned to their home */ + /* We start now checking all chunks to see if we can release any */ + /* We start from Chunks->next, so first chunk is not released */ + /* Recreate nextFreeChunk list from scratch */ + + chunk = Chunks; + while ((freechunk = chunk->next) != NULL) { + age = squid_curtime - freechunk->lastref; + freechunk->nextFreeChunk = NULL; + if (freechunk->inuse_count == 0) + if (age >= maxage) { + chunk->next = freechunk->next; + delete freechunk; + freechunk = NULL; + } + if (chunk->next == NULL) + break; + chunk = chunk->next; + } + + /* Recreate nextFreeChunk list from scratch */ + /* Populate nextFreeChunk list in order of "most filled chunk first" */ + /* in case of equal fill, put chunk in lower ram first */ + /* First (create time) chunk is always on top, no matter how full */ + + chunk = Chunks; + nextFreeChunk = chunk; + chunk->nextFreeChunk = NULL; + + while (chunk->next) { + chunk->next->nextFreeChunk = NULL; + if (chunk->next->inuse_count < chunk_capacity) { + listTail = nextFreeChunk; + while (listTail->nextFreeChunk) { + if (chunk->next->inuse_count > listTail->nextFreeChunk->inuse_count) + break; + if ((chunk->next->inuse_count == listTail->nextFreeChunk->inuse_count) && + (chunk->next->objCache < listTail->nextFreeChunk->objCache)) + break; + listTail = listTail->nextFreeChunk; + } + chunk->next->nextFreeChunk = listTail->nextFreeChunk; + listTail->nextFreeChunk = chunk->next; + } + chunk = chunk->next; + } + /* We started from 2nd chunk. If first chunk is full, remove it */ + if (nextFreeChunk->inuse_count == chunk_capacity) + nextFreeChunk = nextFreeChunk->nextFreeChunk; + + return; +} + +bool +MemPoolChunked::idleTrigger(int shift) const +{ + return getMeter().idle.level > (chunk_capacity << shift); +} + +/* + * Update MemPoolStats struct for single pool + */ +int +MemPoolChunked::getStats(MemPoolStats * stats, int accumulate) +{ + MemChunk *chunk; + int chunks_free = 0; + int chunks_partial = 0; + + if (!accumulate) /* need skip memset for GlobalStats accumulation */ + memset(stats, 0, sizeof(MemPoolStats)); + + clean((time_t) 555555); /* don't want to get chunks released before reporting */ + + stats->pool = this; + stats->label = objectType(); + stats->meter = &getMeter(); + stats->obj_size = obj_size; + stats->chunk_capacity = chunk_capacity; + + /* gather stats for each Chunk */ + chunk = Chunks; + while (chunk) { + if (chunk->inuse_count == 0) + chunks_free++; + else if (chunk->inuse_count < chunk_capacity) + chunks_partial++; + chunk = chunk->next; + } + + stats->chunks_alloc += chunkCount; + stats->chunks_inuse += chunkCount - chunks_free; + stats->chunks_partial += chunks_partial; + stats->chunks_free += chunks_free; + + stats->items_alloc += getMeter().alloc.level; + stats->items_inuse += getMeter().inuse.level; + stats->items_idle += getMeter().idle.level; + + stats->overhead += sizeof(MemPoolChunked) + chunkCount * sizeof(MemChunk) + strlen(objectType()) + 1; + + return getMeter().inuse.level; +} === added file 'lib/MemPoolMalloc.cc' --- lib/MemPoolMalloc.cc 1970-01-01 00:00:00 +0000 +++ lib/MemPoolMalloc.cc 2010-05-28 21:53:06 +0000 @@ -0,0 +1,117 @@ + +/* + * $Id$ + * + * DEBUG: section 63 Low Level Memory Pool Management + * AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins + * + * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from the + * Internet community. Development is led by Duane Wessels of the + * National Laboratory for Applied Network Research and funded by the + * National Science Foundation. Squid is Copyrighted (C) 1998 by + * the Regents of the University of California. Please see the + * COPYRIGHT file for full details. Squid incorporates software + * developed and/or copyrighted by other sources. Please see the + * CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + + +#include "config.h" +#if HAVE_ASSERT_H +#include +#endif + +#include "MemPoolMalloc.h" + +#if HAVE_STRING_H +#include +#endif + +/* + * XXX This is a boundary violation between lib and src.. would be good + * if it could be solved otherwise, but left for now. + */ +extern time_t squid_curtime; + +void * +MemPoolMalloc::allocate() +{ + memMeterInc(meter.alloc); + memMeterInc(meter.inuse); + return xcalloc(1, obj_size); +} + +void +MemPoolMalloc::deallocate(void *obj) +{ + memMeterDec(meter.inuse); + memMeterDec(meter.alloc); + xfree(obj); +} + +/* TODO extract common logic to MemAllocate */ +int +MemPoolMalloc::getStats(MemPoolStats * stats, int accumulate) +{ + if (!accumulate) /* need skip memset for GlobalStats accumulation */ + memset(stats, 0, sizeof(MemPoolStats)); + + stats->pool = this; + stats->label = objectType(); + stats->meter = &getMeter(); + stats->obj_size = obj_size; + stats->chunk_capacity = 0; + + stats->chunks_alloc += 0; + stats->chunks_inuse += 0; + stats->chunks_partial += 0; + stats->chunks_free += 0; + + stats->items_alloc += meter.alloc.level; + stats->items_inuse += meter.inuse.level; + stats->items_idle += meter.idle.level; + + stats->overhead += sizeof(MemPoolMalloc) + strlen(objectType()) + 1; + + return meter.inuse.level; +} + +int +MemPoolMalloc::getInUseCount() +{ + return meter.inuse.level; +} + +MemPoolMalloc::MemPoolMalloc(char const *aLabel, size_t aSize) : MemImplementingAllocator(aLabel, aSize) +{ +} + +bool +MemPoolMalloc::idleTrigger(int shift) const +{ + return false; +} + +void +MemPoolMalloc::clean(time_t maxage) +{ +} + # Begin bundle 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 /XaSf/i7kinChIOSgYcQ