Skip to content

Instantly share code, notes, and snippets.

@nicholaschiasson
Last active July 9, 2019 09:36
Show Gist options
  • Save nicholaschiasson/85cd296f34c5a2728c5a61fb426242f8 to your computer and use it in GitHub Desktop.
Save nicholaschiasson/85cd296f34c5a2728c5a61fb426242f8 to your computer and use it in GitHub Desktop.
Memory Monitors for C++

MemMon Memory Monitors

NOT A GARBAGE COLLECTOR

I decided to try my hand at making my code a bit safer as far as managing weak references goes, so I tried to make something to track references to memory locations. I soon thought that it was a stupid idea and simplified it so that it was simply the memory locations' validity which were being monitored. That way, all I needed to do was essentially a custom null check to see if my weak references were still valid. I tried to do this in the simplest and most aesthetically pleasing way as possible. Naturally, that means (to me) attempting to overload the new and delete keywords for every possible type. Well, since you can't do that, I decided to use macros :)

I also came up with two seperate static classes which are only really different in that they use two different data structures.

SetMemMon

This one represents my first iteration on this idea. Using a set, it simply keeps track of which memory locations are still not freed. I although the next iteration is more useful, I kept this one around just because of the slight speed compromise.

MapMemMon

Using a map, I stored a pointer to the address as the key and the number of bytes to be stored as the value. This way, I could ALSO perform a sizeof on any dynamically allocated arrays, and expanding on that, an element count as well! I am happy with the results and think that I will likely be using MapMemMon all the time in the future.

Testing Results

So for performance testing, among other stress tests, I basically just did a bunch of new array and delete array calls in a loop and compared the clock times with regular new[] and delete[] calls. I would say I am happy enough with the results, given the overhead. Should there be any curiosity, the following is the average of my results for a run of speedtest.cpp:

  • Regular new[] and delete[]: 160 clicks for 1000 allocated and deleted Foo arrays of size 1000
  • SetMemMon new[] and delete[]: 370 clicks for 1000 allocated and deleted Foo arrays of size 1000
  • MapMemMon new[] and delete[]: 580 clicks for 1000 allocated and deleted Foo arrays of size 1000
#include "MapMemMon.h"
using namespace std;
std::map<void *, size_t> * MapMemMon::allocatedMemory = 0;
void MapMemMon::Initialize()
{
if (allocatedMemory == 0)
{
allocatedMemory = new std::map<void *, size_t>();
}
}
void MapMemMon::Cleanup()
{
if (allocatedMemory != 0)
{
delete allocatedMemory;
allocatedMemory = 0;
}
}
bool MapMemMon::Null(void * address)
{
bool rc = (allocatedMemory == 0);
rc = (!rc ? allocatedMemory->find(address) == allocatedMemory->end() : rc);
return rc;
}
size_t MapMemMon::SizeOf(void * data)
{
size_t s = 0;
if (!Null(data))
{
s = (*allocatedMemory)[data];
}
return s;
}
#ifndef MAPMEMMON_H
#define MAPMEMMON_H
#include <map>
/**
* MapMemMon new
*/
#ifndef mmm_new
#define mmm_new(constructor) MapMemMon::New<decltype(constructor)>(new constructor, 1)
#endif // mmm_new
/**
* MapMemMon new array
*/
#ifndef mmm_new_a
#define mmm_new_a(type, n) MapMemMon::New<type>(new type[n](), n)
#endif // mmm_new_a
/**
* MapMemMon delete
*/
#ifndef mmm_delete
#define mmm_delete(data) MapMemMon::Delete<std::remove_reference_t<decltype(*data)>>(data)
#endif // mmm_delete
/**
* MapMemMon null check
*/
#ifndef mmm_null
#define mmm_null MapMemMon::Null
#endif // mmm_null
/**
* MapMemMon memory size
*/
#ifndef mmm_sizeof
#define mmm_sizeof(data) MapMemMon::SizeOf((void *)data)
#endif // mmm_sizeof
/**
* MapMemMon element count
*/
#ifndef mmm_count
#define mmm_count(data) mm_sizeof((void *)data)/sizeof(decltype(*data))
#endif // mmm_count
class MapMemMon
{
public:
static void Initialize();
static void Cleanup();
template<typename T>
static T * New(T * data, size_t count);
template<typename T>
static size_t Delete(T * data);
static bool Null(void * address);
static size_t SizeOf(void * data);
private:
static std::map<void *, size_t> * allocatedMemory;
};
template<typename T>
inline T * MapMemMon::New(T * data, size_t count)
{
if (allocatedMemory == 0)
{
Initialize();
}
if (allocatedMemory != 0 && data != 0 && count > 0)
{
(*allocatedMemory)[(void *)data] = sizeof(T) * count;
}
else
{
if (data != 0)
{
if (count > 1)
{
delete[] data;
}
else
{
delete data;
}
data = 0;
}
}
return data;
}
template<typename T>
inline size_t MapMemMon::Delete(T * data)
{
size_t bytesDeleted = 0;
size_t dataSize = SizeOf(data);
size_t dataCount = dataSize / sizeof(T);
if (allocatedMemory != 0)
{
allocatedMemory->erase((void *)data);
bytesDeleted = dataSize;
if (allocatedMemory->empty()) {
Cleanup();
}
}
if (dataCount > 1)
{
delete[] data;
}
else
{
delete data;
}
return bytesDeleted;
}
#endif // MAPMEMMON_H
#include "SetMemMon.h"
using namespace std;
std::set<void *> * SetMemMon::allocatedMemory = 0;
void SetMemMon::Initialize()
{
if (allocatedMemory == 0)
{
allocatedMemory = new std::set<void *>();
}
}
void SetMemMon::Cleanup()
{
if (allocatedMemory != 0)
{
delete allocatedMemory;
allocatedMemory = 0;
}
}
bool SetMemMon::Null(void * address)
{
bool rc = (allocatedMemory == 0);
rc = (!rc ? allocatedMemory->find(address) == allocatedMemory->end() : rc);
return rc;
}
#ifndef SETMEMMON_H
#define SETMEMMON_H
#include <set>
/**
* SetMemMon new
*/
#ifndef smm_new
#define smm_new(constructor) SetMemMon::New<decltype(constructor)>(new constructor, 1)
#endif // smm_new
/**
* SetMemMon new array
*/
#ifndef smm_new_a
#define smm_new_a(type, n) SetMemMon::New<type>(new type[n](), n)
#endif // smm_new_a
/**
* SetMemMon delete
*/
#ifndef smm_delete
#define smm_delete(data) SetMemMon::Delete<std::remove_reference_t<decltype(*data)>>(data, false)
#endif // smm_delete
/**
* SetMemMon delete array
*/
#ifndef smm_delete_a
#define smm_delete_a(data) SetMemMon::Delete<std::remove_reference_t<decltype(*data)>>(data, true)
#endif // smm_delete_a
/**
* SetMemMon null check
*/
#ifndef smm_null
#define smm_null SetMemMon::Null
#endif // smm_null
class SetMemMon
{
public:
static void Initialize();
static void Cleanup();
template<typename T>
static T * New(T * data, size_t count);
template<typename T>
static void Delete(T * data, bool isArray);
static bool Null(void * address);
private:
static std::set<void *> * allocatedMemory;
};
template<typename T>
inline T * SetMemMon::New(T * data, size_t count)
{
if (allocatedMemory == 0)
{
Initialize();
}
if (allocatedMemory != 0 && data != 0 && count > 0)
{
allocatedMemory->insert((void *)data);
}
else
{
if (data != 0)
{
if (count > 1)
{
delete[] data;
}
else
{
delete data;
}
data = 0;
}
}
return data;
}
template<typename T>
inline void SetMemMon::Delete(T * data, bool isArray)
{
if (allocatedMemory != 0)
{
allocatedMemory->erase((void *)data);
if (allocatedMemory->empty()) {
Cleanup();
}
}
if (isArray)
{
delete[] data;
}
else
{
delete data;
}
}
#endif // SETMEMMON_H
#include <iostream>
#include <ctime>
#include "Foo.h"
#include "SetMemMon.h"
#include "MapMemMon.h"
int main(int argc, char *argv[])
{
int rc = 0;
for (int i = 0; i < 10; ++i)
{
clock_t t_start = clock();
clock_t t_01 = t_start;
clock_t t_02 = t_start;
clock_t t_03 = t_start;
for (int i = 0; i < 1000; ++i)
{
Foo *foo = new Foo[1000]();
delete[] foo;
}
t_01 = clock() - t_start;
for (int i = 0; i < 1000; ++i)
{
Foo *foo = smm_new_a(Foo, 1000);
smm_delete_a(foo);
}
t_02 = clock() - t_start;
for (int i = 0; i < 1000; ++i)
{
Foo *foo = mmm_new_a(Foo, 1000);
mmm_delete(foo);
}
t_03 = clock() - t_start;
printf("%d clicks (%f seconds)\n", t_01, ((float)t_01) / CLOCKS_PER_SEC);
printf("%d clicks (%f seconds)\n", t_02, ((float)t_02) / CLOCKS_PER_SEC);
printf("%d clicks (%f seconds)\n", t_03, ((float)t_03) / CLOCKS_PER_SEC);
printf("\n");
}
return rc;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment