Re: Do you use a garbage collector?
"Ian Collins" <ian-news@hotmail.com> wrote in message
news:66fnchF2kg6m6U2@mid.individual.net...
Chris Thomasson wrote:
I will try and fix it...
Don't bother!
Humm... Well, its too late. The fix was trivial. I was just trying to show
Radii that you can sometimes drastically reduce the number of calls to
new/delete by using the simplest techniques. The little trivial
cache_allocator object I very quickly whipped is a simple example:
_______________________________________________________________________
#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));
}
};
_______________________________________________________________________
I have not really looked at it for any issues, but, AFAICT it just might be
useful to others. I am wondering about undefined behaviors wrt the way I am
using the cache_allocator<T>::node_type union... Can you notice any "major"
issues? I am not a C++ expert! There must be something I overlooked...
;^)
I think I should rewrite the cache_allocator<T>::destroy_dtor() function
like:
void destroy_dtor(T* const obj) {
node_type* node = reinterpret_cast<node_type*>(obj);
try {
obj->~T();
} catch(...) {
sys_destroy(node);
throw;
}
sys_destroy(node);
}
I don't need sys_destroy in the try-block because it will never throw any
exceptions.