Optimization of msgpack::zone size on the stack and deferred memory allocation

This commit is contained in:
Arenoros
2023-08-29 13:33:31 +03:00
parent 37fcaa1264
commit 573ebca3d2

View File

@@ -25,70 +25,51 @@ namespace msgpack {
/// @cond /// @cond
MSGPACK_API_VERSION_NAMESPACE(v1) { MSGPACK_API_VERSION_NAMESPACE(v1) {
/// @endcond /// @endcond
class zone { class zone {
private: private:
struct finalizer { struct finalizer {
finalizer(void (*func)(void*), void* data):m_func(func), m_data(data) {} finalizer(void (*func)(void*), void* data, finalizer* next): m_func(func), m_data(data), m_next(next) {}
void operator()() { m_func(m_data); } void operator()() { m_func(m_data); }
void (*m_func)(void*); void (*m_func)(void*);
void* m_data; void* m_data;
finalizer* m_next{};
}; };
struct finalizer_array { struct finalizer_array {
finalizer_array():m_tail(MSGPACK_NULLPTR), m_end(MSGPACK_NULLPTR), m_array(MSGPACK_NULLPTR) {} finalizer_array(): m_head(MSGPACK_NULLPTR) {}
void call() {
finalizer* fin = m_tail; void call(bool clear = false) {
for(; fin != m_array; --fin) (*(fin-1))(); finalizer* fin = m_head;
finalizer* tmp = nullptr;
while(fin) {
(*(fin-1))();
if (clear) {
tmp = fin;
}
fin = fin->m_next;
if (clear) {
delete tmp;
}
}
} }
~finalizer_array() { ~finalizer_array() {
call(); call(true);
::free(m_array);
} }
void clear() { void clear() {
call(); call(true);
m_tail = m_array;
} }
void push(void (*func)(void* data), void* data) void push(void (*func)(void* data), void* data)
{ {
finalizer* fin = m_tail; m_head = new finalizer(func, data, m_head);
if(fin == m_end) {
push_expand(func, data);
return;
}
fin->m_func = func;
fin->m_data = data;
++m_tail;
} }
void push_expand(void (*func)(void*), void* data) { void pop() {
const size_t nused = static_cast<size_t>(m_end - m_array); auto n = m_head->m_next;
size_t nnext; delete m_head;
if(nused == 0) { m_head = n;
nnext = (sizeof(finalizer) < 72/2) ?
72 / sizeof(finalizer) : 8;
} else {
nnext = nused * 2;
}
finalizer* tmp =
static_cast<finalizer*>(::realloc(m_array, sizeof(finalizer) * nnext));
if(!tmp) {
throw std::bad_alloc();
}
m_array = tmp;
m_end = tmp + nnext;
m_tail = tmp + nused;
new (m_tail) finalizer(func, data);
++m_tail;
} }
finalizer_array(finalizer_array&& other) noexcept
:m_tail(other.m_tail), m_end(other.m_end), m_array(other.m_array) finalizer_array(finalizer_array&& other) noexcept: m_head(other.m_head) {
{ other.m_head = MSGPACK_NULLPTR;
other.m_tail = MSGPACK_NULLPTR;
other.m_end = MSGPACK_NULLPTR;
other.m_array = MSGPACK_NULLPTR;
} }
finalizer_array& operator=(finalizer_array&& other) noexcept finalizer_array& operator=(finalizer_array&& other) noexcept
{ {
@@ -96,10 +77,7 @@ private:
new (this) finalizer_array(std::move(other)); new (this) finalizer_array(std::move(other));
return *this; return *this;
} }
finalizer* m_head;
finalizer* m_tail;
finalizer* m_end;
finalizer* m_array;
private: private:
finalizer_array(const finalizer_array&); finalizer_array(const finalizer_array&);
@@ -109,20 +87,8 @@ private:
chunk* m_next; chunk* m_next;
}; };
struct chunk_list { struct chunk_list {
chunk_list(size_t chunk_size) chunk_list(size_t chunk_size, char* ptr): m_free(chunk_size), m_ptr(ptr), m_head(MSGPACK_NULLPTR) {}
{ ~chunk_list() {
chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + chunk_size));
if(!c) {
throw std::bad_alloc();
}
m_head = c;
m_free = chunk_size;
m_ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
c->m_next = MSGPACK_NULLPTR;
}
~chunk_list()
{
chunk* c = m_head; chunk* c = m_head;
while(c) { while(c) {
chunk* n = c->m_next; chunk* n = c->m_next;
@@ -130,48 +96,35 @@ private:
c = n; c = n;
} }
} }
void clear(size_t chunk_size) void clear(size_t chunk_size, char* ptr)
{ {
chunk* c = m_head; chunk* c = m_head;
while(true) { while(c) {
chunk* n = c->m_next; chunk* n = c->m_next;
if(n) { ::free(c);
::free(c); c = n;
c = n;
} else {
m_head = c;
break;
}
} }
m_head->m_next = MSGPACK_NULLPTR; m_head->m_next = MSGPACK_NULLPTR;
m_free = chunk_size; m_free = chunk_size;
m_ptr = reinterpret_cast<char*>(m_head) + sizeof(chunk); m_ptr = ptr;
}
chunk_list(chunk_list&& other) noexcept
:m_free(other.m_free), m_ptr(other.m_ptr), m_head(other.m_head)
{
other.m_head = MSGPACK_NULLPTR;
}
chunk_list& operator=(chunk_list&& other) noexcept
{
this->~chunk_list();
new (this) chunk_list(std::move(other));
return *this;
} }
size_t m_free; size_t m_free;
char* m_ptr; char* m_ptr;
chunk* m_head; chunk* m_head;
private: private:
chunk_list(chunk_list&& other) noexcept = delete;
chunk_list& operator=(chunk_list&& other) noexcept = delete;
chunk_list(const chunk_list&); chunk_list(const chunk_list&);
chunk_list& operator=(const chunk_list&); chunk_list& operator=(const chunk_list&);
}; };
size_t m_chunk_size; size_t m_chunk_size;
chunk_list m_chunk_list; chunk_list* m_chunk_list{};
finalizer_array m_finalizer_array; finalizer_array m_finalizer_array;
public: public:
zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE); zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE);
~zone();
public: public:
void* allocate_align(size_t size, size_t align = MSGPACK_ZONE_ALIGN); void* allocate_align(size_t size, size_t align = MSGPACK_ZONE_ALIGN);
@@ -223,53 +176,75 @@ private:
static char* get_aligned(char* ptr, size_t align); static char* get_aligned(char* ptr, size_t align);
chunk_list& get_chank_lst();
char* allocate_expand(size_t size); char* allocate_expand(size_t size);
}; };
inline zone::zone(size_t chunk_size):m_chunk_size(chunk_size), m_chunk_list(m_chunk_size) inline zone::zone(size_t chunk_size):m_chunk_size(chunk_size), m_chunk_list(MSGPACK_NULLPTR)
{ {
} }
inline zone::~zone()
{
if(m_chunk_list) {
m_chunk_list->~chunk_list();
::free(m_chunk_list);
m_chunk_list = MSGPACK_NULLPTR;
}
}
inline char* zone::get_aligned(char* ptr, size_t align) inline char* zone::get_aligned(char* ptr, size_t align)
{ {
MSGPACK_ASSERT(align != 0 && (align & (align - 1)) == 0); // align must be 2^n (n >= 0) MSGPACK_ASSERT(align != 0 && (align & (align - 1)) == 0); // align must be 2^n (n >= 0)
return return
reinterpret_cast<char*>( reinterpret_cast<char*>(
reinterpret_cast<uintptr_t>(ptr + (align - 1)) & ~static_cast<uintptr_t>(align - 1) reinterpret_cast<uintptr_t>(ptr + (align - 1)) & ~static_cast<uintptr_t>(align - 1)
); );
}
inline zone::chunk_list& zone::get_chank_lst()
{
if (!m_chunk_list) {
auto ptr = ::malloc(sizeof(chunk_list) + m_chunk_size);
if (!ptr)
throw std::bad_alloc();
m_chunk_list = new (ptr) chunk_list(m_chunk_size, reinterpret_cast<char*>(ptr) + sizeof(chunk_list));
}
return *m_chunk_list;
} }
inline void* zone::allocate_align(size_t size, size_t align) inline void* zone::allocate_align(size_t size, size_t align)
{ {
char* aligned = get_aligned(m_chunk_list.m_ptr, align); chunk_list& chank_lst = get_chank_lst();
size_t adjusted_size = size + static_cast<size_t>(aligned - m_chunk_list.m_ptr); char* aligned = get_aligned(chank_lst.m_ptr, align);
if (m_chunk_list.m_free < adjusted_size) { size_t adjusted_size = size + static_cast<size_t>(aligned - chank_lst.m_ptr);
if (chank_lst.m_free < adjusted_size) {
size_t enough_size = size + align - 1; size_t enough_size = size + align - 1;
char* ptr = allocate_expand(enough_size); char* ptr = allocate_expand(enough_size);
aligned = get_aligned(ptr, align); aligned = get_aligned(ptr, align);
adjusted_size = size + static_cast<size_t>(aligned - m_chunk_list.m_ptr); adjusted_size = size + static_cast<size_t>(aligned - chank_lst.m_ptr);
} }
m_chunk_list.m_free -= adjusted_size; chank_lst.m_free -= adjusted_size;
m_chunk_list.m_ptr += adjusted_size; chank_lst.m_ptr += adjusted_size;
return aligned; return aligned;
} }
inline void* zone::allocate_no_align(size_t size) inline void* zone::allocate_no_align(size_t size) {
{ chunk_list& chank_lst = get_chank_lst();
char* ptr = m_chunk_list.m_ptr; char* ptr = chank_lst.m_ptr;
if(m_chunk_list.m_free < size) { if(chank_lst.m_free < size) {
ptr = allocate_expand(size); ptr = allocate_expand(size);
} }
m_chunk_list.m_free -= size; chank_lst.m_free -= size;
m_chunk_list.m_ptr += size; chank_lst.m_ptr += size;
return ptr; return ptr;
} }
inline char* zone::allocate_expand(size_t size) inline char* zone::allocate_expand(size_t size)
{ {
chunk_list* const cl = &m_chunk_list; chunk_list& cl = get_chank_lst();
size_t sz = m_chunk_size; size_t sz = m_chunk_size;
while(sz < size) { while(sz < size) {
@@ -286,10 +261,10 @@ inline char* zone::allocate_expand(size_t size)
char* ptr = reinterpret_cast<char*>(c) + sizeof(chunk); char* ptr = reinterpret_cast<char*>(c) + sizeof(chunk);
c->m_next = cl->m_head; c->m_next = cl.m_head;
cl->m_head = c; cl.m_head = c;
cl->m_free = sz; cl.m_free = sz;
cl->m_ptr = ptr; cl.m_ptr = ptr;
return ptr; return ptr;
} }
@@ -308,7 +283,9 @@ inline void zone::push_finalizer(msgpack::unique_ptr<T> obj)
inline void zone::clear() inline void zone::clear()
{ {
m_finalizer_array.clear(); m_finalizer_array.clear();
m_chunk_list.clear(m_chunk_size); if (m_chunk_list) {
m_chunk_list->clear(m_chunk_size, reinterpret_cast<char*>(m_chunk_list) + sizeof(chunk_list));
}
} }
inline void zone::swap(zone& o) inline void zone::swap(zone& o)
@@ -328,10 +305,10 @@ void zone::object_destruct(void* obj)
static_cast<T*>(obj)->~T(); static_cast<T*>(obj)->~T();
} }
inline void zone::undo_allocate(size_t size) inline void zone::undo_allocate(size_t size) {
{ chunk_list& cl = get_chank_lst();
m_chunk_list.m_ptr -= size; cl.m_ptr -= size;
m_chunk_list.m_free += size; cl.m_free += size;
} }
@@ -348,7 +325,7 @@ T* zone::allocate(Args... args)
try { try {
return new (x) T(args...); return new (x) T(args...);
} catch (...) { } catch (...) {
--m_finalizer_array.m_tail; m_finalizer_array.pop();
undo_allocate(sizeof(T)); undo_allocate(sizeof(T));
throw; throw;
} }