Re: Do you use a garbage collector?
"Chris Thomasson" <cristom@comcast.net> wrote in message
news:TMCdnanpTvavOp_VnZ2dnUVZ_uCinZ2d@comcast.com...
"Chris Thomasson" <cristom@comcast.net> wrote in message
news:RrGdnZr1EfRYPZ_VnZ2dnUVZ_qOknZ2d@comcast.com...
"Razii" <DONTwhatevere3e@hotmail.com> wrote in message
news:r84504tsla9563r79shakgbg4r37ju4vjs@4ax.com...
On Sun, 13 Apr 2008 23:41:29 +0200, "Bo Persson" <bop@gmb.dk> wrote:
Isn't that what I said? The new and delete operators are not used very
often in the code. Perhaps only once each in std::allocator, where
they don't allocate int-sized objects, and never 10 million times in a
loop.
std::vector has new and delete. They are still there and must be there
every time memory is allocated dynamically.
That's why the benchmark is silly - you would never do anything like
that in real C++ code.
It's not silly. It's a benchmark that tests dynamic memory allocation
and GC performance.
[...]
Your test is silly because it leaks a whole lot of memory! WTF? This means
that your not taking advantage of the cache allocator. Or any caching in
the new/delete implementation for that matter!
YIKES!
I will try and fix it...
Okay. The fix was simple enough:
____________________________________________________________________
#include <new>
#include <cstddef>
#include <cassert>
#if ! defined(NDEBUG)
# include <cstdio>
# define DBG_PRINTF(mp_exp) std::printf mp_exp
#else
# define DBG_PRINTF(mp_exp)
#endif
template<typename T>
class cache_allocator {
union node_type {
T m_obj;
node_type* m_next;
};
node_type* m_head;
std::size_t m_depth;
std::size_t const m_max_depth;
public:
cache_allocator(
std::size_t const prime = 128,
std::size_t const max_depth = 1024
): m_head(NULL),
m_depth(0),
m_max_depth(max_depth) {
for (std::size_t i = 0; i < prime && i < max_depth; ++i) {
node_type* const node = reinterpret_cast<node_type*>
(::operator new(sizeof(*node)));
node->m_next = m_head;
m_head = node;
++m_depth;
DBG_PRINTF(("(%p/%d)-Cache Prime\n", (void*)node, m_depth));
}
assert(m_depth <= max_depth);
}
~cache_allocator() throw() {
node_type* node = m_head;
while (node) {
node_type* const next = node->m_next;
--m_depth;
DBG_PRINTF(("(%p/%d)-Cache Teardown\n", (void*)node, m_depth));
::operator delete(node);
node = next;
}
assert(! m_depth);
}
void sys_destroy(node_type* const node) throw() {
if (m_depth < m_max_depth) {
node->m_next = m_head;
m_head = node;
++m_depth;
DBG_PRINTF(("(%p/%d)-Cache Store\n", (void*)node, m_depth));
} else {
DBG_PRINTF(("(%p/%d)-Cache Overflow\n", (void*)node, m_depth));
::operator delete(node);
}
}
public:
T* create_ctor() {
node_type* node = m_head;
if (! node) {
node = new (::operator new(sizeof(*node))) node_type;
DBG_PRINTF(("(%p/%d)-Cache Miss!\n", (void*)node, m_depth));
return &node->m_obj;
}
m_head = node->m_next;
--m_depth;
DBG_PRINTF(("(%p/%d)-Cache Hit!\n", (void*)node, m_depth));
return new (&node->m_obj) T;
}
void destroy_dtor(T* const obj) {
node_type* node = reinterpret_cast<node_type*>(obj);
try {
obj->~T();
sys_destroy(node);
} catch(...) {
sys_destroy(node);
throw;
}
}
public:
T* create_raw() {
node_type* node = m_head;
if (! node) {
node = reinterpret_cast<node_type*>
(::operator new(sizeof(*node)));
DBG_PRINTF(("(%p/%d)-Cache Miss!\n", (void*)node, m_depth));
return &node->m_obj;
}
m_head = node->m_next;
--m_depth;
DBG_PRINTF(("(%p/%d)-Cache Hit!\n", (void*)node, m_depth));
return &node->m_obj;
}
void destroy_raw(T* const obj) throw() {
sys_destroy(reinterpret_cast<node_type*>(obj));
}
};
// Your Test...
#include <ctime>
#include <iostream>
#define TREE_CREATE_CACHE_CTOR g_tree_malloc.create_ctor
#define TREE_CREATE_CACHE_RAW g_tree_malloc.create_raw
#define TREE_CREATE_NEW new Tree
#define TREE_DESTROY_CACHE_CTOR(ptr) g_tree_malloc.destroy_dtor(ptr)
#define TREE_DESTROY_CACHE_RAW(ptr) g_tree_malloc.destroy_raw(ptr)
#define TREE_DESTROY_DELETE(ptr) delete ptr
#define TREE_CREATE TREE_CREATE_CACHE_RAW
#define TREE_DESTROY TREE_DESTROY_CACHE_RAW
// #define RAZII_MEMORY_LEAK_VERSION
struct Tree
{
Tree *left;
Tree *right;
};
static cache_allocator<Tree> g_tree_malloc(100000, 250000);
static int g_allocs = 0;
static int g_frees = 0;
Tree *CreateTree(int n)
{
if(n <= 0) return NULL;
Tree *t = TREE_CREATE();
++g_allocs;
t->left = CreateTree(n - 1);
t->right = CreateTree(n - 1);
return t;
}
#if defined(RAZII_MEMORY_LEAK_VERSION)
void DeleteTree(Tree *t)
{
if(t)
{
TREE_DESTROY(t->left);
TREE_DESTROY(t->right);
TREE_DESTROY(t);
g_frees += 3;
}
}
#else
void DeleteTree(Tree *t) {
if (t) {
DeleteTree(t->left);
DeleteTree(t->right);
TREE_DESTROY(t);
++g_frees;
}
}
#endif
int main(int argc, char *argv[])
{
clock_t start=clock();
for(int i = 0;i < 15;i++) DeleteTree(CreateTree(22));
clock_t endt=clock();
std::cout <<"Time: " <<
double(endt-start)/CLOCKS_PER_SEC * 1000 << " ms\n";
if (g_allocs != g_frees) {
std::cout << "YOU HAVE LEAKED " << g_allocs - g_frees << " ITEMS!\n";
}
return 0;
}
____________________________________________________________________
Alls you have to do is define RAZII_MEMORY_LEAK_VERSION to see just how many
objects you were leaking! It was a shi%load. Anyway, try this out, and
report the numbers.