// ============================================================================
// == ==
// == 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