#include #include "MiscSupport.h" #include "ThreadMonitor.h" #include "SimpleLogFile.h" #include "ThreadClass.h" #include "ZLibMalloc.h" /* The main point of this is to keep statistics. We have a few random memory * leaks, and I want to make sure that's not coming from here. * * We always had some ability to ask for the memory status. Now we're * automatically adding that to the log. So we can watch and see if these * numbers are changing while the size of the program changes. * * We used to call FixedMalloc.h to give us the actual memory. FixedMalloc * generally seems to cause more problems than it solves. The original point * was to deal with messages sent from one thread to the next. It's very * common for us to request a lot of memory of exactly the same size from * one thread, and delete those in a different thread. ZLib is different. * We're almost certainly going to be returning each block of memory in the * same thread as we requested that block. We typically have 2 or more * threads using ZLib, but each using it seperately. * * We've copied a lot of the logic from FixedMalloc.C directly into this * file. We've made one major change. FixedMalloc explicitly had one pool * of free memory and that memory was shared between threads using a mutex. * This implemention has a different memory pool assigned to each thread. That * gives us a small performance boost. This arrangement is slightly better * than the standard malloc which will mostly use one memory pool per thread, * but occasionally threads will swap memory pools. */ class ZLibMalloc : private ThreadMonitor::Extra { private: unsigned long long _bytesInUse; unsigned long long _chunksInUse; unsigned long long _bytesFreed; unsigned long long _chunksFreed; unsigned long long _bytesReserved; std::unordered_map< size_t, void * > _freeList; // For ThreadMonitor::Extra virtual std::string getInfoForThreadMonitor() { TclList result; result<<"zLib" <<"bytes in use"<<_bytesInUse <<"chunks in use"<<_chunksInUse <<"bytes freed"<<_bytesFreed <<"chunks freed"<<_chunksFreed <<"bytes reserved"<<_bytesReserved <<"chunk sizes"<<_freeList.size(); return result; } void *fixedMalloc(size_t size) { size = std::max(sizeof(void *), sizeof(size_t) + size); // The next line came from FixedMalloc.C. We made sure each request was // aligned to an even 8 byte boundary. That was mostly aimed at memory // that we might try to access atomically. That's a non-issue for zlib. //size = roundUp(size); void *&top = _freeList[size]; if (!top) { TclList msg; msg<getName(); sendToLogFile(msg); const int perBlock = 25; size_t blockSize = size * (size_t)perBlock; char *next = new char[blockSize]; if (next) { _bytesReserved += blockSize; for (int count = 0; count < perBlock; count++) { *(void **)next = top; top = next; next += size; } } } void *result = 0; if (top) { _bytesReserved -= size; result = top; top = *(void **)result; *(size_t *)result = size; result = (char *)result + sizeof(size_t); } return result; } void fixedFree(void *mem) { if (mem) { mem = (char *)mem - sizeof(size_t); size_t size = *(size_t *)mem; _bytesReserved += size; void *&top = _freeList[size]; *(void **)mem = top; top = mem; } } size_t fixedMallocSize(void *mem) { if (mem) { return *(((size_t *)mem)-1); } else { return 0; } } ZLibMalloc() :_bytesInUse(0), _chunksInUse(0), _bytesFreed(0), _chunksFreed(0), _bytesReserved(0) { ThreadMonitor::find().add(this); } public: static ZLibMalloc &instance() { static __thread ZLibMalloc *i = new ZLibMalloc(); return *i; } void *malloc(unsigned items, unsigned size) { void *result = fixedMalloc(items * size); _bytesInUse += fixedMallocSize(result); _chunksInUse++; return result; } void free(void *address) { size_t size = fixedMallocSize(address); _bytesInUse -= size; _chunksInUse--; _bytesFreed += size; _chunksFreed++; fixedFree(address); } }; void *zlibMalloc(void *, unsigned items, unsigned size) { return ZLibMalloc::instance().malloc(items, size); } void zlibFree(void *, void *address) { return ZLibMalloc::instance().free(address); }