// ============================================================================ // == == // == AMDiS - Adaptive multidimensional simulations == // == == // ============================================================================ // == == // == crystal growth group == // == == // == Stiftung caesar == // == Ludwig-Erhard-Allee 2 == // == 53175 Bonn == // == germany == // == == // ============================================================================ // == == // == http://www.caesar.de/cg/AMDiS == // == == // ============================================================================ /** \file MemoryManager.h */ /** \defgroup Memory Memory management module * @{ @} * * \brief * Memory monitoring and preallocation. */ #ifndef AMDIS_MEMORYMANAGER_H #define AMDIS_MEMORYMANAGER_H #include "Global.h" #include "MemoryPool.h" #include #include #include #include #include #include namespace AMDiS { class MemoryMonitor; class InstanceCounterBase; template class InstanceCounter; // ============================================================================ // ===== class MemoryManagerBase ============================================== // ============================================================================ /** \ingroup Memory * \brief * MemoryManagerBase is the template type independent base class of * MemoryManager. It holds a static vector of all concrete MemoryManagers, * so a memory statistic for all managed types can be printed by * \ref printStatistics(). A concrete MemoryManager class in general is * a static class, so memory operations can be done without an instance of * a MemoryManager. But MemoryManagerBase should be able to handle * different concrete MemoryManagers without knowing their exact types. * Therefore a concrete MemoryManager must have a singleton instance too, * which gives MemoryManagerBase access to its static members by pointing * to this singleton instance. * * */ class MemoryManagerBase { public: /** \brief * Destructor does nothing */ virtual ~MemoryManagerBase() {} /** \brief * Prints the number of currently allocated bytes (\ref byteCount) and for * all MemoryManagers in \ref memoryManagers the number of currently * allocated instances. */ static void printStatistics() { FUNCNAME("MemoryManagerBase::printStatistics()"); const char* pl="s\n"; const char* sg="\n"; sort(memoryManagers.begin(), memoryManagers.end(), orderInstanceCount()); std::vector::iterator it; MSG("================================================================\n"); MSG("memory statistics:\n"); MSG("==================\n"); MSG("byte count: %ld\n", byteCount); for (it = memoryManagers.begin(); it != memoryManagers.end(); it++) { if ((*it)->instanceCount != 0) { MSG("%s, id = %ld : %ld instance%s", (*it)->getName().c_str(), (*it)->typeId, (*it)->instanceCount,((*it)->instanceCount > 1)?pl:sg); // if((*it)->instanceCount > 1) // MSG("s"); // MSG("\n"); } } MSG("================================================================\n"); } /** \brief * Must be overriden for concrete MemoryManagers. Returns the name of * the managed data type. Can be accessed via the singleton pointer of * MemoryManager. */ virtual std::string getName() = 0; /** \brief * Returns the number of currently allocated instances (\ref instanceCount). */ inline unsigned long int getInstanceCount() { return instanceCount; } /** \brief * Returns the number of currently allocated bytes (\ref typedByteCount). */ inline unsigned long int getTypedByteCount() { return typedByteCount; } /** \brief * Returns a new InstanceCounter for concrete type * Must be overloaded by a concrete MemoryManager. */ virtual InstanceCounterBase* newInstanceCounter() = 0; /** \brief * Register a new MemoryMonitor */ static void addMemoryMonitor(MemoryMonitor* monitor); /** \brief * Remove a registered MemoryMonitor */ static void removeMemoryMonitor(MemoryMonitor* monitor); protected: /** \brief * Used in \ref printStatistics() to sort the MemoryManagers by the number * of currently allocated instances. */ class orderInstanceCount : public std::binary_function { public: bool operator()(MemoryManagerBase *a, MemoryManagerBase *b) { return (a->getInstanceCount() > b->getInstanceCount()); } }; protected: /** \brief * Number of currently allocated instances in a concrete MemoryManager. * Note that this member is not static, so every MemoryManager has * its own instanceCount which can be accessed via the singleton pointer. */ unsigned long int instanceCount; /** \brief * byte count for a special type */ unsigned long int typedByteCount; /** \brief * ID of the managed data type. Usefull if no typeid is accessable. Note * that this member is not static, so every MemoryManager has * its own typeId which can be accessed via the singleton pointer. */ unsigned long int typeId; /** \brief * Number of totally allocated bytes by all managers in \ref memoryManagers. */ static unsigned long int byteCount; /** \brief * Vector of all MemoryManagers. Every concrete MemoryManager must add * a pointer to itself to this vector when constructed. */ static std::vector memoryManagers; /** \brief * List of all registered MemoryMonitors */ static std::vector memoryMonitors; /** \brief * Used to create unique IDs for every concrete MemoryManager */ static unsigned long int idCounter; }; // ============================================================================ // ===== class MemoryManager ================================================== // ============================================================================ /** \ingroup Memory * \brief * The main task of a MemoryManager is to count the number of instances * of the managed type T. Every instance of T then must be allocated * by \ref MemoryManager::getMemory(). * The memory is freed by \ref MemoryManager::freeMemory() * If a class is \ref MEMORY_MANAGED * the operators new, delete, new[] and delete[] are overloaded, so that * the corresponding MemoryManager calls are performed. If you use then the * \ref NEW and \ref DELETE macros additionally file name and line number * of the macro call are passed to the MemoryManager and will be printed * on every call of \ref getMemory() if \ref printInfo is true. if you * allocate and deallocate memory for a MEMORY_MANAGED class by new and delete * this information will not be present, but the calls are still passed * through MemoryManager. * If you want to allocate memory for scalar data types with MemoryManager * you can use the macros \ref GET_MEMORY and \ref FREE_MEMORY which * will call \ref MemoryManager::getMemory()/ * \ref MemoryManager::freeMemory() with file name and line number * information. In this case no new- or delete-operators are called, so * even no constructors or destructors are called. * If \ref preallocated is true, all memory allocation and deallocation * operations are deligated to MemoryPool. * * */ template class MemoryManager : public MemoryManagerBase { protected: /** \brief * Constructor */ MemoryManager() {} public: /** \brief * Initialisation of the MemoryManager. Creates the \ref singleton instance * with initialized typeId and instanceCount (see \ref MemoryManagerBase), * and adds a pointer to \ref singleton to * \ref MemoryManagerBase::memoryManagers */ static void init(); /** \brief * Returns memory for one instance of T. If MemoryManager is not yet * initialized, \ref init() will be called. The byte and instance counter * are incremented and if \ref printInfo is true, information about this * memory allocation are printed. If \ref preallocatable is true, the * allocation is deligated to MemoryPool, otherwise it is done here. */ static T* getMemory(size_t s, const char* filename, int line) { FUNCNAME("MemoryManager::getMemory()"); if (!singleton) init(); byteCount += s; singleton->instanceCount += s/sizeof(T); singleton->typedByteCount += s; if (printInfo) { if (filename && line) { MSG("FILE: %s, LINE: %d - ", filename, line); } Msg::print("%s::getMemory : %d instances, total : %d instances\n", singleton->getName().c_str(), s/sizeof(T), static_cast(singleton->instanceCount)); } return preallocatable ? reinterpret_cast(MemoryPool::getMemory(s)) : reinterpret_cast(new char[s]); } /** \brief * Frees memory which was allocated by \ref getMemory(). If \ref printInfo * is true, information about this deallocation is printed */ static void freeMemory(T* mem, size_t s) { FUNCNAME("MemoryManager::freeMemory()"); TEST_EXIT_DBG(singleton)("not initialized\n"); byteCount -= s; singleton->instanceCount -= s/sizeof(T); singleton->typedByteCount -= s; if(printInfo) { MSG("%s::freeMemory : %d instances, total : %d instances\n", singleton->getName().c_str(), s/sizeof(T), static_cast(singleton->instanceCount)); } preallocatable ? MemoryPool::freeMemory(mem, s) : delete [] (reinterpret_cast(mem)); } /** \brief * Returns name of T by demangle the mangled typeid of T. */ inline std::string getName() { return typeid(T).name(); } /** \brief * Sets \ref preallocatable */ static void setPreallocatable(bool prealloc) { preallocatable = prealloc; } /** \brief * Sets \ref printInfo */ static void setPrintInfo(bool p) { printInfo = p; } /** \brief * Returns \ref printInfo */ static bool getPrintInfo() { return printInfo; } /** \brief * Returns the pointer to the \ref singleton instance of MemoryManager */ static MemoryManager* getSingleton() { return singleton; } /** \brief * Implementation of \ref MemoryManagerBase::newInstanceCounter() */ InstanceCounterBase* newInstanceCounter() { return new InstanceCounter; } protected: /** \brief * The one and only instance of a MemoryManager for this type T */ static MemoryManager *singleton; /** \brief * Determines whether memory allocation is deligated to MemoryPool */ static bool preallocatable; /** \brief * Determines whether information should be printed when allocating and * deallocationg memory. */ static bool printInfo; }; template MemoryManager *MemoryManager::singleton = NULL; template bool MemoryManager::preallocatable = false; template bool MemoryManager::printInfo = false; // ============================================================================ // ===== class InstanceCounterBase ============================================ // ============================================================================ /** \ingroup Memory * \brief * Template type independent interface for InstanceCounter. A MemoryMonitor * holds a list of InstanceCounters for the different data types. * * */ class InstanceCounterBase { public: /** \brief * To be overloaded by a concrete InstanceCounter */ virtual long int getInstanceCount() = 0; /** \brief * To be overloaded by a concrete InstanceCounter */ virtual std::string getTypeName() = 0; /** \brief * Destructor */ virtual ~InstanceCounterBase() {}; }; // ============================================================================ // ===== class InstanceCounter ================================================ // ============================================================================ /** \ingroup Memory * \brief * When an InstanceCounter is created, it remembers the current number * of T instances in MemoryManager. After that one can get the difference * between this remembered number and the current number in MemoryManager * by \ref getInstanceCount() at every time. A MemoryMonitor holds a list * of InstanceCounters for the different data types. */ template class InstanceCounter : public InstanceCounterBase { public: /** \brief * Creates an InstanceCounter and stores the current instanceCount of * MemoryManager in \ref oldInstanceCount. */ InstanceCounter() : oldInstanceCount(MemoryManager::getSingleton()->getInstanceCount()) {} /** \brief * Sets \ref oldInstanceCount to the current number of instances in * MemoryManager */ inline void reset() { oldInstanceCount = MemoryManager::getSingleton()->getInstanceCount(); } /** \brief * Returns the difference between the current instanceCount and * \ref oldInstanceCount */ long int getInstanceCount() { return (MemoryManager::getSingleton()->getInstanceCount() - oldInstanceCount); } /** \brief * Returns the name of T. Calls MemoryManager::getName() */ std::string getTypeName() { return MemoryManager::getSingleton()->getName(); } protected: /** \brief * Number of instances in MemoryManager at creation time of this * InstanceCounter. Can be reseted by \ref reset() */ long int oldInstanceCount; }; // ============================================================================ // ===== class MemoryMonitor ================================================== // ============================================================================ /** \ingroup Memory * \brief * A MemoryMonitor counts from its construction time the number of allocated * instances for every memory managed data type. It holds a list with one * InstanceCounter for every managed data type. This list is updated by * MemoryManagerBase. Herefore MemoryManagerBase needs a list of all * MemoryMonitors. So a MemoryMonitor must register itself when constructed * and deregister when deleted. * * */ class MemoryMonitor { public: /** \brief * Constructor. Registers itself with MemoryMonitorBase. */ MemoryMonitor(const char *name_, const char *filename_=NULL) : name(name_), filename(filename_) { MemoryManagerBase::addMemoryMonitor(this); } /** \brief * Destructor. Prints statistics and deregisters itself with * MemoryManagerBase. */ ~MemoryMonitor() { printStatistics(); MemoryManagerBase::removeMemoryMonitor(this); } /** \brief * Prints a statistic for every data type with instanceCount - * \ref oldInstanceCount != 0. */ void printStatistics() { FUNCNAME("MemoryMonitor::printStatistics()"); std::ostream *oldOut; static bool fileOpen = false; if (filename && !fileOpen) { oldOut = Msg::getOutStream(); Msg::open_file(filename, std::ios::out); fileOpen = true; } std::vector::iterator it; sort(instanceCounters.begin(), instanceCounters.end(), orderInstanceCount()); MSG("MemoryMonitor %s - statistics since creation:\n", name.c_str()); for (it = instanceCounters.begin(); it != instanceCounters.end(); it++) { if ((*it)->getInstanceCount() != 0) { MSG("%s : %d instance", (*it)->getTypeName().c_str(), static_cast((*it)->getInstanceCount())); if (static_cast((*it)->getInstanceCount()) > 1) Msg::print("s"); MSG("\n"); } } if (filename) { Msg::change_out(oldOut); } } /** \brief * Adds a InstanceCounter to \ref instanceCounters. Used by MemoryManagerBase * to update the list of InstanceCounters */ inline void addInstanceCounter(InstanceCounterBase* counter) { instanceCounters.push_back(counter); } protected: /** \brief * Used in \ref printStatistics() to sort the InstanceCounters by the number * of currently allocated instances. */ class orderInstanceCount : public std::binary_function { public: bool operator()(InstanceCounterBase *a, InstanceCounterBase *b) { return (a->getInstanceCount() > b->getInstanceCount()); } }; protected: /** \brief * Name of the monitor */ std::string name; /** \brief * Filename */ const char *filename; /** \brief * List with InstanceCounters for every data type */ std::vector instanceCounters; friend class MemoryManagerBase; }; template void MemoryManager::init() { FUNCNAME("MemoryManager::init()"); TEST_EXIT_DBG(singleton == NULL)("already initialized\n"); singleton = new MemoryManager; singleton->typeId = idCounter++; singleton->instanceCount = 0; memoryManagers.push_back(singleton); std::vector::iterator it; for (it = memoryMonitors.begin(); it != memoryMonitors.end(); it++) { (*it)->addInstanceCounter(new InstanceCounter); } } #if 0 /** \brief * If MEMORY_MANAGED(T) is called in the public declaration of class T * the operators new, delete, new[], and delete[] are overloaded: * new and new[] calls \ref MemoryManager::getMemory(), * delete and delete[] calls \ref MemoryManager::freeMemory(). * If you use the macro \ref NEW for memory allocation, additional information * about the file name and line number are given to the new operators which * are printed if \ref MemoryManager::printInfo is true. */ #define MEMORY_MANAGED(className) \ void *operator new(size_t s) \ { \ return MemoryManager::getMemory(s, NULL, 0); \ } \ void *operator new(size_t s, const char *filename, int line) \ { \ return MemoryManager::getMemory(s, filename, line); \ } \ void operator delete(void *mem, size_t s) \ { MemoryManager::freeMemory(static_cast(mem), s);}; \ void *operator new[](size_t s, const char *filename=NULL, int line=0) \ { \ return MemoryManager::getMemory(s, filename, line); \ } \ void operator delete[](void *mem, size_t s) \ { MemoryManager::freeMemory(static_cast(mem), s); }; \ void *operator new(size_t, void *ptr) { \ return ptr; \ } /** \brief * Calls the new operator with additional file name and line number information */ #define NEW new /** \brief * Calls the delete operator. */ #define DELETE delete /** \brief * Allocates memory for number instances of the given type. Additionally * file name and line number information are given to the MemoryManager. * Use this macro to allocate scalar data types with a MemoryManager. */ #define GET_MEMORY(typename, number) \ MemoryManager::getMemory((number) * sizeof(typename), \ __FILE__,__LINE__) /** \brief * Deallocation of memory allocated with \ref GET_MEMORY */ #define FREE_MEMORY(ptr, typename, number) \ MemoryManager::freeMemory(ptr, (number) * sizeof(typename)) #endif // use these macros to deactivate memory management #define MEMORY_MANAGED(className) ; #define NEW new #define DELETE delete #define GET_MEMORY(typename, number) new typename[number] #define FREE_MEMORY(ptr, typename, number) delete [] ptr } #endif // AMDIS_MEMORYMANAGER_H