# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: henrik@henriknordstrom.net-20100529010638-\ # 2cuq58qvx58gf5ej # target_branch: http://www.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 75a36627905a58be51174b7e0929dca3a85d4921 # timestamp: 2010-05-29 03:07:21 +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-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 00:59:35 +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,195 +114,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. @@ -364,54 +124,16 @@ 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-28 23:51:08 +0000 @@ -580,6 +580,9 @@ 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; @@ -627,10 +630,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 +686,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 +695,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 +715,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 +749,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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