# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: henrik@henriknordstrom.net-20100529013101-\ # wsx8ra6jggw9nibl # target_branch: http://www.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 97dccb4964a9a07345462887b7dbc0c52274b136 # timestamp: 2010-05-29 03:32:22 +0200 # base_revision_id: henrik@henriknordstrom.net-20100528215627-\ # agkq7l8rn7lertwg # # Begin patch === modified file 'configure.in' --- configure.in 2010-05-28 14:15:11 +0000 +++ configure.in 2010-05-29 01:31:01 +0000 @@ -1953,19 +1953,18 @@ ]) dnl Disable "memPools" code -AC_ARG_ENABLE(mempools, - AS_HELP_STRING([--disable-mempools], - [Disable memPools. Note that this option now simply sets the - default behaviour. Specific classes can override this +AC_ARG_ENABLE(chunkedmempools, + AS_HELP_STRING([--enable-chunkedmempools], + [Enable experimental chunked memPools. Note that this option + simply sets the default behaviour. Specific classes can override this at runtime, and only lib/MemPool.c needs to be altered to change the squid-wide default for all classes.]), [ SQUID_YESNO([$enableval], - [--disable-mempools option takes no arguments]) + [--enable-chunkedmempools option takes no arguments]) ]) -# notice: the definition's value meaning is INVERTED -SQUID_DEFINE_BOOL(USE_MEMPOOLS,${enable_mempools:=no}, - [Enable internal Memory Pools support (experimental)]) -AC_MSG_NOTICE([MemPools enabled: $enable_mempools]) +SQUID_DEFINE_BOOL(USE_CHUNKEDMEMPOOLS,${enable_chunkedmempools:=no}, + [Enable chunked Memory Pools support (experimental)]) +AC_MSG_NOTICE([Chunked MemPools enabled: $enable_chunkedmempools]) dnl Enable WIN32 Service compile mode AC_ARG_ENABLE(win32-service, === modified file 'include/MemPool.h' --- include/MemPool.h 2010-03-21 03:08:26 +0000 +++ include/MemPool.h 2010-05-29 00:59:35 +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 @@ -97,17 +96,20 @@ class MemPoolMeter { public: + MemPoolMeter(); void flush(); MemMeter alloc; MemMeter inuse; MemMeter idle; - /** account Allocations */ + + /** history Allocations */ + mgb_t gb_allocated; + mgb_t gb_oallocated; + + /** account Saved Allocations */ mgb_t gb_saved; - /** history Allocations */ - mgb_t gb_osaved; - /** account Free calls */ mgb_t gb_freed; }; @@ -130,13 +132,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 +192,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; @@ -325,6 +320,7 @@ { public: MemImplementingAllocator(char const *aLabel, size_t aSize); + virtual ~MemImplementingAllocator(); virtual MemPoolMeter const &getMeter() const; virtual MemPoolMeter &getMeter(); virtual void flushMetersFull(); @@ -342,111 +338,23 @@ 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: + virtual void deallocate(void *, bool aggressive) = 0; MemPoolMeter meter; + int memPID; public: MemImplementingAllocator *next; public: size_t alloc_calls; size_t free_calls; + size_t saved_calls; size_t obj_size; }; /// \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 +444,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-29 00:59:35 +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 *, bool aggressive); +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-29 00:59:35 +0000 @@ -0,0 +1,49 @@ +#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); + ~MemPoolMalloc(); + 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 *, bool aggressive); +private: + Stack freelist; +}; + + +#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-29 01:31:01 +0000 @@ -33,63 +33,16 @@ * */ -/* - * 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 "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 #if HAVE_STRING_H #include @@ -107,10 +60,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,253 +114,26 @@ 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. */ MemPools::MemPools() : pools(NULL), mem_idle_limit(2 * MB), - poolCount (0), defaultIsChunked (USE_MEMPOOLS && !RUNNING_ON_VALGRIND) + poolCount (0), defaultIsChunked (USE_CHUNKEDMEMPOOLS && !RUNNING_ON_VALGRIND) { char *cfg = getenv("MEMPOOLS"); if (cfg) defaultIsChunked = atoi(cfg); -#if HAVE_MALLOPT && M_MMAP_MAX - mallopt(M_MMAP_MAX, MEM_MAX_MMAP_CHUNKS); -#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 +142,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,17 +161,18 @@ 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_allocated.count += calls; + alloc_calls = 0; + } + calls = saved_calls; + if (calls) { meter.gb_saved.count += calls; - memMeterAdd(meter.inuse, calls); - memMeterDel(meter.idle, calls); - alloc_calls = 0; + saved_calls = 0; } } @@ -491,6 +180,7 @@ MemImplementingAllocator::flushMetersFull() { flushMeters(); + getMeter().gb_allocated.bytes = getMeter().gb_allocated.count * obj_size; getMeter().gb_saved.bytes = getMeter().gb_saved.count * obj_size; getMeter().gb_freed.bytes = getMeter().gb_freed.count * obj_size; } @@ -501,11 +191,21 @@ alloc.level = 0; inuse.level = 0; idle.level = 0; + gb_allocated.count = 0; + gb_allocated.bytes = 0; + gb_oallocated.count = 0; + gb_oallocated.bytes = 0; gb_saved.count = 0; gb_saved.bytes = 0; gb_freed.count = 0; gb_freed.bytes = 0; } + +MemPoolMeter::MemPoolMeter() +{ + flush(); +} + /* * Updates all pool counters, and recreates TheMeter totals from all pools */ @@ -523,8 +223,10 @@ memMeterAdd(TheMeter.alloc, pool->getMeter().alloc.level * pool->obj_size); memMeterAdd(TheMeter.inuse, pool->getMeter().inuse.level * pool->obj_size); memMeterAdd(TheMeter.idle, pool->getMeter().idle.level * pool->obj_size); + TheMeter.gb_allocated.count += pool->getMeter().gb_allocated.count; TheMeter.gb_saved.count += pool->getMeter().gb_saved.count; TheMeter.gb_freed.count += pool->getMeter().gb_freed.count; + TheMeter.gb_allocated.bytes += pool->getMeter().gb_allocated.bytes; TheMeter.gb_saved.bytes += pool->getMeter().gb_saved.bytes; TheMeter.gb_freed.bytes += pool->getMeter().gb_freed.bytes; } @@ -532,20 +234,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) @@ -559,127 +247,10 @@ { assert(obj != NULL); (void) VALGRIND_CHECK_MEM_IS_ADDRESSABLE(obj, obj_size); - deallocate(obj); + deallocate(obj, MemPools::GetInstance().mem_idle_limit == 0); ++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 +278,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 +299,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 +318,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 +333,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) { @@ -935,8 +407,39 @@ next(NULL), alloc_calls(0), free_calls(0), + saved_calls(0), obj_size(RoundedSize(aSize)) { + memPID = ++Pool_id_counter; + + MemImplementingAllocator *last_pool; + + assert(aLabel != NULL && aSize); + /* 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; +} + +MemImplementingAllocator::~MemImplementingAllocator() +{ + MemImplementingAllocator *find_pool, *prev_pool; + + assert(MemPools::GetInstance().pools != NULL && "Called MemImplementingAllocator::~MemImplementingAllocator, 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; } void === added file 'lib/MemPoolChunked.cc' --- lib/MemPoolChunked.cc 1970-01-01 00:00:00 +0000 +++ lib/MemPoolChunked.cc 2010-05-29 00:59:35 +0000 @@ -0,0 +1,501 @@ + +/* + * $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 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; + + setChunkSize(MEM_CHUNK_SIZE); + +#if HAVE_MALLOPT && M_MMAP_MAX + mallopt(M_MMAP_MAX, MEM_MAX_MMAP_CHUNKS); +#endif +} + +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; + + saved_calls++; + + /* 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 */ + saved_calls--; // compensate for the ++ above + 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; + + flushMetersFull(); + clean(0); + assert(meter.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. */ + +} + +int +MemPoolChunked::getInUseCount() +{ + return meter.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, bool aggressive) +{ + 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 meter.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 = &meter; + 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 += meter.alloc.level; + stats->items_inuse += meter.inuse.level; + stats->items_idle += meter.idle.level; + + stats->overhead += sizeof(MemPoolChunked) + chunkCount * sizeof(MemChunk) + strlen(objectType()) + 1; + + return meter.inuse.level; +} === added file 'lib/MemPoolMalloc.cc' --- lib/MemPoolMalloc.cc 1970-01-01 00:00:00 +0000 +++ lib/MemPoolMalloc.cc 2010-05-29 01:06:38 +0000 @@ -0,0 +1,142 @@ + +/* + * $Id$ + * + * DEBUG: section 63 Low Level Memory Pool Management + * AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins, Henrik Nordstrom + * + * 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() +{ + void *obj = freelist.pop(); + if (obj) { + memMeterDec(meter.idle); + saved_calls++; + } else { + obj = xcalloc(1, obj_size); + memMeterInc(meter.alloc); + } + memMeterInc(meter.inuse); + return obj; +} + +void +MemPoolMalloc::deallocate(void *obj, bool aggressive) +{ + memMeterDec(meter.inuse); + if (aggressive) { + xfree(obj); + memMeterDec(meter.alloc); + } else { + if (doZeroOnPush) + memset(obj, 0, obj_size); + memMeterInc(meter.idle); + freelist.push_back(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 = &meter; + 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) +{ +} + +MemPoolMalloc::~MemPoolMalloc() +{ + assert(meter.inuse.level == 0 && "While trying to destroy pool"); + clean(0); +} + +bool +MemPoolMalloc::idleTrigger(int shift) const +{ + return freelist.count >> (shift ? 8 : 0); +} + +void +MemPoolMalloc::clean(time_t maxage) +{ + while (void *obj = freelist.pop()) { + memMeterDec(meter.idle); + memMeterDec(meter.alloc); + xfree(obj); + } +} + === modified file 'src/mem.cc' --- src/mem.cc 2010-03-21 03:08:26 +0000 +++ src/mem.cc 2010-05-29 01:19:53 +0000 @@ -580,27 +580,38 @@ MemPoolMeter *pm = mp_st->meter; const char *delim = "\t "; +#if HAVE_IOMANIP + stream.setf(std::ios_base::fixed); +#endif stream << std::setw(20) << std::left << mp_st->label << delim; stream << std::setw(4) << std::right << mp_st->obj_size << delim; /* Chunks */ - stream << std::setw(4) << toKB(mp_st->obj_size * mp_st->chunk_capacity) << delim; - stream << std::setw(4) << mp_st->chunk_capacity << delim; - if (mp_st->chunk_capacity) { + stream << std::setw(4) << toKB(mp_st->obj_size * mp_st->chunk_capacity) << delim; + stream << std::setw(4) << mp_st->chunk_capacity << delim; + needed = mp_st->items_inuse / mp_st->chunk_capacity; if (mp_st->items_inuse % mp_st->chunk_capacity) needed++; excess = mp_st->chunks_inuse - needed; + + stream << std::setw(4) << mp_st->chunks_alloc << delim; + stream << std::setw(4) << mp_st->chunks_inuse << delim; + stream << std::setw(4) << mp_st->chunks_free << delim; + stream << std::setw(4) << mp_st->chunks_partial << delim; + stream << std::setprecision(3) << xpercent(excess, needed) << delim; + } else { + stream << delim; + stream << delim; + stream << delim; + stream << delim; + stream << delim; + stream << delim; + stream << delim; } - - stream << std::setw(4) << mp_st->chunks_alloc << delim; - stream << std::setw(4) << mp_st->chunks_inuse << delim; - stream << std::setw(4) << mp_st->chunks_free << delim; - stream << std::setw(4) << mp_st->chunks_partial << delim; - stream << std::setprecision(3) << xpercent(excess, needed) << delim; /* * Fragmentation calculation: * needed = inuse.level / chunk_capacity @@ -627,10 +638,10 @@ stream << toKB(mp_st->obj_size * pm->idle.hwater_level) << delim; /* saved */ stream << (int)pm->gb_saved.count << delim; - stream << std::setprecision(3) << xpercent(pm->gb_saved.count, AllMeter->gb_saved.count) << delim; - stream << std::setprecision(3) << xpercent(pm->gb_saved.bytes, AllMeter->gb_saved.bytes) << delim; - stream << std::setprecision(3) << xdiv(pm->gb_saved.count - pm->gb_osaved.count, xm_deltat) << "\n"; - pm->gb_osaved.count = pm->gb_saved.count; + stream << std::setprecision(3) << xpercent(pm->gb_saved.count, AllMeter->gb_allocated.count) << delim; + stream << std::setprecision(3) << xpercent(pm->gb_saved.bytes, AllMeter->gb_allocated.bytes) << delim; + stream << std::setprecision(3) << xdiv(pm->gb_allocated.count - pm->gb_oallocated.count, xm_deltat) << "\n"; + pm->gb_oallocated.count = pm->gb_allocated.count; } static int @@ -683,7 +694,7 @@ "In Use\t\t\t\t\t" "Idle\t\t\t" "Allocations Saved\t\t\t" - "Hit Rate\t" + "Rate\t" "\n" " \t (bytes)\t" "KB/ch\t obj/ch\t" @@ -692,7 +703,7 @@ "(#)\t (KB)\t high (KB)\t high (hrs)\t %alloc\t" "(#)\t (KB)\t high (KB)\t" "(#)\t %cnt\t %vol\t" - "(#) / sec\t" + "(#)/sec\t" "\n"; xm_deltat = current_dtime - xm_time; xm_time = current_dtime; @@ -712,7 +723,7 @@ if (!mp_stats.pool) /* pool destroyed */ continue; - if (mp_stats.pool->getMeter().gb_saved.count > 0) /* this pool has been used */ + if (mp_stats.pool->getMeter().gb_allocated.count > 0) /* this pool has been used */ sortme[npools++] = mp_stats; else not_used++; @@ -746,7 +757,7 @@ PoolReport(&mp_stats, mp_total.TheMeter, stream); /* Cumulative */ - stream << "Cumulative allocated volume: "<< double_to_str(buf, 64, mp_total.TheMeter->gb_saved.bytes) << "\n"; + stream << "Cumulative allocated volume: "<< double_to_str(buf, 64, mp_total.TheMeter->gb_allocated.bytes) << "\n"; /* overhead */ stream << "Current overhead: " << mp_total.tot_overhead << " bytes (" << std::setprecision(3) << xpercent(mp_total.tot_overhead, mp_total.TheMeter->inuse.level) << "%)\n"; # Begin bundle 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