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:
parent
d930c909f2
commit
4240e3d668
@ -31,6 +31,8 @@
|
|||||||
#ifndef COWDATA_H_
|
#ifndef COWDATA_H_
|
||||||
#define COWDATA_H_
|
#define COWDATA_H_
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "core/os/memory.h"
|
#include "core/os/memory.h"
|
||||||
#include "core/safe_refcount.h"
|
#include "core/safe_refcount.h"
|
||||||
|
|
||||||
@ -194,6 +196,7 @@ void CowData<T>::_unref(void *p_data) {
|
|||||||
return; // still in use
|
return; // still in use
|
||||||
// clean up
|
// clean up
|
||||||
|
|
||||||
|
if (!__has_trivial_destructor(T)) {
|
||||||
uint32_t *count = _get_size();
|
uint32_t *count = _get_size();
|
||||||
T *data = (T *)(count + 1);
|
T *data = (T *)(count + 1);
|
||||||
|
|
||||||
@ -201,6 +204,7 @@ void CowData<T>::_unref(void *p_data) {
|
|||||||
// call destructors
|
// call destructors
|
||||||
data[i].~T();
|
data[i].~T();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// free mem
|
// free mem
|
||||||
Memory::free_static((uint8_t *)p_data, true);
|
Memory::free_static((uint8_t *)p_data, true);
|
||||||
@ -226,10 +230,14 @@ void CowData<T>::_copy_on_write() {
|
|||||||
T *_data = (T *)(mem_new);
|
T *_data = (T *)(mem_new);
|
||||||
|
|
||||||
// initialize new elements
|
// 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));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < current_size; i++) {
|
||||||
memnew_placement(&_data[i], T(_get_data()[i]));
|
memnew_placement(&_data[i], T(_get_data()[i]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_unref(_ptr);
|
_unref(_ptr);
|
||||||
_ptr = _data;
|
_ptr = _data;
|
||||||
@ -275,23 +283,26 @@ Error CowData<T>::resize(int p_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// construct the newly created elements
|
// construct the newly created elements
|
||||||
|
|
||||||
|
if (!__has_trivial_constructor(T)) {
|
||||||
T *elems = _get_data();
|
T *elems = _get_data();
|
||||||
|
|
||||||
for (int i = *_get_size(); i < p_size; i++) {
|
for (int i = *_get_size(); i < p_size; i++) {
|
||||||
|
|
||||||
memnew_placement(&elems[i], T);
|
memnew_placement(&elems[i], T);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*_get_size() = p_size;
|
*_get_size() = p_size;
|
||||||
|
|
||||||
} else if (p_size < size()) {
|
} else if (p_size < size()) {
|
||||||
|
|
||||||
|
if (!__has_trivial_destructor(T)) {
|
||||||
// deinitialize no longer needed elements
|
// deinitialize no longer needed elements
|
||||||
for (uint32_t i = p_size; i < *_get_size(); i++) {
|
for (uint32_t i = p_size; i < *_get_size(); i++) {
|
||||||
|
|
||||||
T *t = &_get_data()[i];
|
T *t = &_get_data()[i];
|
||||||
t->~T();
|
t->~T();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true);
|
void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true);
|
||||||
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
|
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
|
||||||
|
@ -116,7 +116,9 @@ void memdelete(T *p_class) {
|
|||||||
|
|
||||||
if (!predelete_handler(p_class))
|
if (!predelete_handler(p_class))
|
||||||
return; // doesn't want to be deleted
|
return; // doesn't want to be deleted
|
||||||
|
if (!__has_trivial_destructor(T))
|
||||||
p_class->~T();
|
p_class->~T();
|
||||||
|
|
||||||
Memory::free_static(p_class, false);
|
Memory::free_static(p_class, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +127,9 @@ void memdelete_allocator(T *p_class) {
|
|||||||
|
|
||||||
if (!predelete_handler(p_class))
|
if (!predelete_handler(p_class))
|
||||||
return; // doesn't want to be deleted
|
return; // doesn't want to be deleted
|
||||||
|
if (!__has_trivial_destructor(T))
|
||||||
p_class->~T();
|
p_class->~T();
|
||||||
|
|
||||||
A::free(p_class);
|
A::free(p_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,12 +154,14 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
|
|||||||
ERR_FAIL_COND_V(!mem, failptr);
|
ERR_FAIL_COND_V(!mem, failptr);
|
||||||
*(mem - 1) = p_elements;
|
*(mem - 1) = p_elements;
|
||||||
|
|
||||||
|
if (!__has_trivial_constructor(T)) {
|
||||||
T *elems = (T *)mem;
|
T *elems = (T *)mem;
|
||||||
|
|
||||||
/* call operator new */
|
/* call operator new */
|
||||||
for (size_t i = 0; i < p_elements; i++) {
|
for (size_t i = 0; i < p_elements; i++) {
|
||||||
new (&elems[i], sizeof(T), p_descr) T;
|
new (&elems[i], sizeof(T), p_descr) T;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (T *)mem;
|
return (T *)mem;
|
||||||
}
|
}
|
||||||
@ -177,12 +183,14 @@ void memdelete_arr(T *p_class) {
|
|||||||
|
|
||||||
uint64_t *ptr = (uint64_t *)p_class;
|
uint64_t *ptr = (uint64_t *)p_class;
|
||||||
|
|
||||||
|
if (!__has_trivial_destructor(T)) {
|
||||||
uint64_t elem_count = *(ptr - 1);
|
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);
|
Memory::free_static(ptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user