Optimizations for trivial types

Relying on various compiler primitives we can reduce the work done
in our memory allocators and CowData. For types with trivial ctors or
dtors we can skip looping over all elements when creating, resizing,
and destroying lists of objects.

These primitives are supported by clang, msvc, and GCC. However, once
we've moved to C++11 we can rely on several std:: primitives that do
the same thing and are standardized.

In my testing the extra conditionals introduced here get removed from
the generated program entirely as the results for these primitives is
known at compile time.
This commit is contained in:
Hein-Pieter van Braam 2019-01-03 05:56:41 +01:00
parent d930c909f2
commit 4240e3d668
2 changed files with 44 additions and 25 deletions

View file

@ -31,6 +31,8 @@
#ifndef COWDATA_H_
#define COWDATA_H_
#include <string.h>
#include "core/os/memory.h"
#include "core/safe_refcount.h"
@ -194,12 +196,14 @@ void CowData<T>::_unref(void *p_data) {
return; // still in use
// clean up
uint32_t *count = _get_size();
T *data = (T *)(count + 1);
if (!__has_trivial_destructor(T)) {
uint32_t *count = _get_size();
T *data = (T *)(count + 1);
for (uint32_t i = 0; i < *count; ++i) {
// call destructors
data[i].~T();
for (uint32_t i = 0; i < *count; ++i) {
// call destructors
data[i].~T();
}
}
// free mem
@ -226,9 +230,13 @@ void CowData<T>::_copy_on_write() {
T *_data = (T *)(mem_new);
// initialize new elements
for (uint32_t i = 0; i < current_size; i++) {
if (__has_trivial_copy(T)) {
memcpy(mem_new, _ptr, current_size * sizeof(T));
memnew_placement(&_data[i], T(_get_data()[i]));
} else {
for (uint32_t i = 0; i < current_size; i++) {
memnew_placement(&_data[i], T(_get_data()[i]));
}
}
_unref(_ptr);
@ -275,22 +283,25 @@ Error CowData<T>::resize(int p_size) {
}
// construct the newly created elements
T *elems = _get_data();
for (int i = *_get_size(); i < p_size; i++) {
if (!__has_trivial_constructor(T)) {
T *elems = _get_data();
memnew_placement(&elems[i], T);
for (int i = *_get_size(); i < p_size; i++) {
memnew_placement(&elems[i], T);
}
}
*_get_size() = p_size;
} else if (p_size < size()) {
// deinitialize no longer needed elements
for (uint32_t i = p_size; i < *_get_size(); i++) {
T *t = &_get_data()[i];
t->~T();
if (!__has_trivial_destructor(T)) {
// deinitialize no longer needed elements
for (uint32_t i = p_size; i < *_get_size(); i++) {
T *t = &_get_data()[i];
t->~T();
}
}
void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true);

View file

@ -116,7 +116,9 @@ void memdelete(T *p_class) {
if (!predelete_handler(p_class))
return; // doesn't want to be deleted
p_class->~T();
if (!__has_trivial_destructor(T))
p_class->~T();
Memory::free_static(p_class, false);
}
@ -125,7 +127,9 @@ void memdelete_allocator(T *p_class) {
if (!predelete_handler(p_class))
return; // doesn't want to be deleted
p_class->~T();
if (!__has_trivial_destructor(T))
p_class->~T();
A::free(p_class);
}
@ -150,11 +154,13 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
ERR_FAIL_COND_V(!mem, failptr);
*(mem - 1) = p_elements;
T *elems = (T *)mem;
if (!__has_trivial_constructor(T)) {
T *elems = (T *)mem;
/* call operator new */
for (size_t i = 0; i < p_elements; i++) {
new (&elems[i], sizeof(T), p_descr) T;
/* call operator new */
for (size_t i = 0; i < p_elements; i++) {
new (&elems[i], sizeof(T), p_descr) T;
}
}
return (T *)mem;
@ -177,12 +183,14 @@ void memdelete_arr(T *p_class) {
uint64_t *ptr = (uint64_t *)p_class;
uint64_t elem_count = *(ptr - 1);
if (!__has_trivial_destructor(T)) {
uint64_t elem_count = *(ptr - 1);
for (uint64_t i = 0; i < elem_count; i++) {
for (uint64_t i = 0; i < elem_count; i++) {
p_class[i].~T();
}
}
p_class[i].~T();
};
Memory::free_static(ptr, true);
}