Performance enhancements

This commit is contained in:
John Wellbelove 2017-02-23 23:14:58 +00:00
parent 0508d796bc
commit 46a11d1747
2 changed files with 338 additions and 370 deletions

View File

@ -43,6 +43,7 @@ SOFTWARE.
#include "type_traits.h"
#include "parameter_type.h"
#include "error_handler.h"
#include "memory.h"
#ifdef ETL_COMPILER_GCC
#pragma GCC diagnostic ignored "-Wunused-variable"
@ -72,14 +73,6 @@ namespace etl
typedef size_t size_type;
typedef typename std::iterator_traits<iterator>::difference_type difference_type;
private:
// Type trait to allow selection of simple algorithms for simple types.
template <typename U>
struct is_simple_type : integral_constant<bool, etl::is_fundamental<U>::value || etl::is_pointer<U>::value>
{
};
protected:
typedef typename parameter_type<T>::type parameter_t;
@ -92,7 +85,7 @@ namespace etl
//*********************************************************************
iterator begin()
{
return &p_buffer[0];
return p_buffer;
}
//*********************************************************************
@ -101,7 +94,7 @@ namespace etl
//*********************************************************************
const_iterator begin() const
{
return &p_buffer[0];
return p_buffer;
}
//*********************************************************************
@ -110,7 +103,7 @@ namespace etl
//*********************************************************************
iterator end()
{
return &p_buffer[current_size];
return p_end;
}
//*********************************************************************
@ -119,7 +112,7 @@ namespace etl
//*********************************************************************
const_iterator end() const
{
return &p_buffer[current_size];
return p_end;
}
//*********************************************************************
@ -128,7 +121,7 @@ namespace etl
//*********************************************************************
const_iterator cbegin() const
{
return &p_buffer[0];
return p_buffer;
}
//*********************************************************************
@ -137,7 +130,7 @@ namespace etl
//*********************************************************************
const_iterator cend() const
{
return &p_buffer[current_size];
return p_end;
}
//*********************************************************************
@ -202,42 +195,7 @@ namespace etl
//*********************************************************************
void resize(size_t new_size)
{
ETL_ASSERT(new_size <= MAX_SIZE, ETL_ERROR(vector_full));
// Choose the algorithm according to type.
// The unused branch should be optimised away.
if (is_simple_type<value_type>::value)
{
// Size up if necessary.
if (current_size < new_size)
{
std::fill_n(&p_buffer[current_size], new_size - current_size, 0);
}
current_size = new_size;
}
else
{
// Size up or size down?
if (new_size > current_size)
{
// Up.
while (current_size < new_size)
{
create_element();
}
}
else if (new_size < current_size)
{
// Down.
while (current_size > new_size)
{
destroy_element();
}
}
}
resize(new_size, T());
}
//*********************************************************************
@ -247,42 +205,65 @@ namespace etl
///\param new_size The new size.
///\param value The value to fill new elements with. Default = default constructed value.
//*********************************************************************
void resize(size_t new_size, T value)
template <typename U = T>
typename etl::enable_if<etl::has_trivial_constructor<U>::value, void>::type
resize(size_t new_size, T value)
{
ETL_ASSERT(new_size <= MAX_SIZE, ETL_ERROR(vector_full));
// Choose the algorithm according to type.
// The unused branch should be optimised away.
if (is_simple_type<value_type>::value)
{
// Size up if necessary.
if (current_size < new_size)
{
std::fill_n(&p_buffer[current_size], new_size - current_size, value);
}
const size_t current_size = size();
size_t delta = (current_size < new_size) ? new_size - current_size : current_size - new_size;
current_size = new_size;
if (current_size < new_size)
{
std::fill_n(p_end, delta, value);
#if defined(ETL_DEBUG)
construct_count += delta;
#endif
}
else
{
// Size up or size down?
if (new_size > current_size)
{
// Up.
while (current_size < new_size)
{
create_element(value);
}
}
else if (new_size < current_size)
{
// Down.
while (current_size > new_size)
{
destroy_element();
}
}
p_end -= delta;
#if defined(ETL_DEBUG)
construct_count -= delta;
#endif
}
p_end = p_buffer + new_size;
}
//*********************************************************************
/// Resizes the vector.
/// If asserts or exceptions are enabled and the new size is larger than the
/// maximum then a vector_full is thrown.
///\param new_size The new size.
///\param value The value to fill new elements with. Default = default constructed value.
//*********************************************************************
template <typename U = T>
typename etl::enable_if<!etl::has_trivial_constructor<U>::value, void>::type
resize(size_t new_size, T value)
{
ETL_ASSERT(new_size <= MAX_SIZE, ETL_ERROR(vector_full));
const size_t current_size = size();
size_t delta = (current_size < new_size) ? new_size - current_size : current_size - new_size;
if (current_size < new_size)
{
etl::uninitialized_fill_n(p_end, delta, value);
#if defined(ETL_DEBUG)
construct_count += delta;
#endif
}
else
{
etl::destroy_n(p_end - delta, delta);
#if defined(ETL_DEBUG)
construct_count -= delta;
#endif
}
p_end = p_buffer + new_size;
}
//*********************************************************************
@ -320,7 +301,7 @@ namespace etl
//*********************************************************************
reference at(size_t i)
{
ETL_ASSERT(i < current_size, ETL_ERROR(vector_out_of_bounds));
ETL_ASSERT(i < size(), ETL_ERROR(vector_out_of_bounds));
return p_buffer[i];
}
@ -332,7 +313,7 @@ namespace etl
//*********************************************************************
const_reference at(size_t i) const
{
ETL_ASSERT(i < current_size, ETL_ERROR(vector_out_of_bounds));
ETL_ASSERT(i < size(), ETL_ERROR(vector_out_of_bounds));
return p_buffer[i];
}
@ -342,7 +323,7 @@ namespace etl
//*********************************************************************
reference front()
{
return p_buffer[0];
return *p_buffer;
}
//*********************************************************************
@ -351,7 +332,7 @@ namespace etl
//*********************************************************************
const_reference front() const
{
return p_buffer[0];
return *p_buffer;
}
//*********************************************************************
@ -360,7 +341,7 @@ namespace etl
//*********************************************************************
reference back()
{
return p_buffer[current_size - 1];
return *(p_end - 1);
}
//*********************************************************************
@ -369,7 +350,7 @@ namespace etl
//*********************************************************************
const_reference back() const
{
return p_buffer[current_size - 1];
return *(p_end - 1);
}
//*********************************************************************
@ -400,34 +381,18 @@ namespace etl
template <typename TIterator>
void assign(TIterator first, TIterator last)
{
#if defined(_DEBUG) || defined(DEBUG)
#if defined(ETL_DEBUG)
difference_type count = std::distance(first, last);
ETL_ASSERT(static_cast<size_t>(count) <= MAX_SIZE, ETL_ERROR(vector_full));
#endif
initialise();
// Choose the algorithm according to type.
// The unused branch should be optimised away.
if (is_simple_type<value_type>::value)
{
while (first != last)
{
p_buffer[current_size++] = *first++;
}
}
else
{
T* p = &p_buffer[0];
while (first != last)
{
new (p) T(*first);
++p;
++first;
++current_size;
++construct_count;
}
}
#if defined(ETL_DEBUG)
p_end = etl::uninitialized_copy(first, last, p_buffer, construct_count);
#else
p_end = etl::uninitialized_copy(first, last, p_buffer);
#endif
}
//*********************************************************************
@ -438,32 +403,15 @@ namespace etl
//*********************************************************************
void assign(size_t n, parameter_t value)
{
initialise();
ETL_ASSERT(n <= MAX_SIZE, ETL_ERROR(vector_full));
// Choose the algorithm according to type.
// The unused branch should be optimised away.
if (is_simple_type<value_type>::value)
{
while (n > 0)
{
p_buffer[current_size++] = value;
--n;
}
}
else
{
T* p = &p_buffer[0];
while (n > 0)
{
new (p) T(value);
++p;
--n;
++current_size;
++construct_count;
}
}
initialise();
#if defined(ETL_DEBUG)
p_end = etl::uninitialized_fill_n(p_buffer, n, value, construct_count);
#else
p_end = etl::uninitialized_fill_n(p_buffer, n, value);
#endif
}
//*************************************************************************
@ -481,9 +429,10 @@ namespace etl
void push_back()
{
#if defined(ETL_CHECK_PUSH_POP)
ETL_ASSERT(current_size != MAX_SIZE, ETL_ERROR(vector_full));
ETL_ASSERT(size() != MAX_SIZE, ETL_ERROR(vector_full));
#endif
create_element();
create_back();
}
//*********************************************************************
@ -494,9 +443,9 @@ namespace etl
void push_back(parameter_t value)
{
#if defined(ETL_CHECK_PUSH_POP)
ETL_ASSERT(current_size != MAX_SIZE, ETL_ERROR(vector_full));
ETL_ASSERT(size() != MAX_SIZE, ETL_ERROR(vector_full));
#endif
create_element(value);
create_back(value);
}
//*************************************************************************
@ -506,9 +455,9 @@ namespace etl
void pop_back()
{
#if defined(ETL_CHECK_PUSH_POP)
ETL_ASSERT(current_size > 0, ETL_ERROR(vector_empty));
ETL_ASSERT(size() > 0, ETL_ERROR(vector_empty));
#endif
destroy_element();
destroy_back();
}
//*********************************************************************
@ -519,18 +468,17 @@ namespace etl
//*********************************************************************
iterator insert(iterator position, parameter_t value)
{
ETL_ASSERT((current_size)+1 <= MAX_SIZE, ETL_ERROR(vector_full));
ETL_ASSERT(size() + 1 <= MAX_SIZE, ETL_ERROR(vector_full));
if (position != end())
if (position == end())
{
T* p_last = &p_buffer[current_size - 1];
create_element(*p_last);
std::copy_backward(position, p_last, p_last + 1);
*position = value;
create_back(value);
}
else
{
create_element(value);
create_back(back());
std::copy_backward(position, p_end - 1, p_end);
*position = value;
}
return position;
@ -544,17 +492,16 @@ namespace etl
///\param value The value to insert.
//*********************************************************************
template <typename U = T>
typename etl::enable_if<is_simple_type<U>::value, void>::type
typename etl::enable_if<etl::has_trivial_constructor<U>::value, void>::type
insert(iterator position, size_t n, parameter_t value)
{
ETL_ASSERT((current_size + n) <= MAX_SIZE, ETL_ERROR(vector_full));
T* p_end = &p_buffer[current_size];
ETL_ASSERT((size() + n) <= MAX_SIZE, ETL_ERROR(vector_full));
std::copy_backward(position, p_end, p_end + n);
std::fill_n(position, n, value);
current_size += n;
construct_count += n;
p_end += n;
}
//*********************************************************************
@ -565,173 +512,163 @@ namespace etl
///\param value The value to insert.
//*********************************************************************
template <typename U = T>
typename etl::enable_if<!is_simple_type<U>::value, void>::type
typename etl::enable_if<!etl::has_trivial_constructor<U>::value, void>::type
insert(iterator position, size_t n, parameter_t value)
{
ETL_ASSERT((current_size + n) <= MAX_SIZE, ETL_ERROR(vector_full));
U v = value;
ETL_ASSERT((size() + n) <= MAX_SIZE, ETL_ERROR(vector_full));
size_t insert_n = n;
size_t insert_begin = std::distance(begin(), position);
size_t insert_end = insert_begin + insert_n;
size_t new_size = current_size + n;
size_t old_move_n = new_size - insert_end;
size_t new_copy_n = insert_n;
// Copy old data.
size_t copy_old_n;
size_t construct_old_n;
iterator p_construct_old;
// Move old.
if (old_move_n > 0)
if (insert_end > size())
{
size_t index = new_size;
T* from = &p_buffer[size() - 1];
T* to = &p_buffer[size() - 1 + insert_n];
while (index > insert_end)
{
if (index > size())
{
// Construct.
new (to--) U(*from--);
++construct_count;
}
else
{
// Copy
*to-- = *from--;
}
--index;
}
copy_old_n = 0;
construct_old_n = size() - insert_begin;
p_construct_old = p_buffer + insert_end;
}
else
{
copy_old_n = size() - insert_begin - insert_n;
construct_old_n = insert_n;
p_construct_old = p_end;
}
size_t copy_new_n = construct_old_n;
size_t construct_new_n = insert_n - copy_new_n;
#if defined(ETL_DEBUG)
// Construct old.
etl::uninitialized_copy_n(p_end - construct_old_n, construct_old_n, p_construct_old, construct_count);
// Copy old.
etl::copy_n(p_buffer + insert_begin, copy_old_n, p_buffer + insert_end);
// Construct new.
etl::uninitialized_fill_n(p_end, construct_new_n, value, construct_count);
// Copy new.
if (new_copy_n > 0)
std::fill_n(p_buffer + insert_begin, copy_new_n, value);
#else
// Construct old.
etl::uninitialized_copy_n(p_end - construct_old_n, construct_old_n, p_construct_old);
// Copy old.
etl::copy_n(p_buffer + insert_begin, copy_old_n, p_buffer + insert_end);
// Construct new.
etl::uninitialized_fill_n(p_end, construct_new_n, value);
// Copy new.
std::fill_n(p_buffer + insert_begin, copy_new_n, value);
#endif
p_end += n;
}
//*********************************************************************
/// Inserts a range of values to the vector.
/// If asserts or exceptions are enabled, emits vector_full if the vector does not have enough free space.
/// For fundamental and pointer types.
///\param position The position to insert before.
///\param first The first element to add.
///\param last The last + 1 element to add.
//*********************************************************************
template <class TIterator, typename U = T>
typename etl::enable_if<etl::has_trivial_constructor<U>::value, void>::type
insert(iterator position, TIterator first, TIterator last)
{
size_t count = std::distance(first, last);
ETL_ASSERT((size() + count) <= MAX_SIZE, ETL_ERROR(vector_full));
construct_count += count;
if (position == end())
{
size_t index = insert_begin;
T* begin = &p_buffer[insert_begin];
T* end = &p_buffer[insert_end];
while (begin != end)
{
if (index > size())
{
// Construct.
new (begin++) U(v);
++construct_count;
}
else
{
// Copy.
*begin++ = v;
}
++index;
}
p_end = std::copy(first, last, p_end);
}
else
{
std::copy_backward(position, p_end, p_end + count);
std::copy(first, last, position);
p_end += count;
}
}
//*********************************************************************
/// Inserts a range of values to the vector.
/// If asserts or exceptions are enabled, emits vector_full if the vector does not have enough free space.
/// For fundamental and pointer types.
///\param position The position to insert before.
///\param first The first element to add.
///\param last The last + 1 element to add.
//*********************************************************************
template <class TIterator, typename U = T>
typename etl::enable_if<!etl::has_trivial_constructor<U>::value, void>::type
insert(iterator position, TIterator first, TIterator last)
{
size_t count = std::distance(first, last);
ETL_ASSERT((size() + count) <= MAX_SIZE, ETL_ERROR(vector_full));
size_t insert_n = count;
size_t insert_begin = std::distance(begin(), position);
size_t insert_end = insert_begin + insert_n;
// Copy old data.
size_t copy_old_n;
size_t construct_old_n;
iterator p_construct_old;
if (insert_end > size())
{
copy_old_n = 0;
construct_old_n = size() - insert_begin;
p_construct_old = p_buffer + insert_end;
}
else
{
copy_old_n = size() - insert_begin - insert_n;
construct_old_n = insert_n;
p_construct_old = p_end;
}
size_t copy_new_n = construct_old_n;
size_t construct_new_n = insert_n - copy_new_n;
current_size += n;
}
#if defined(ETL_DEBUG)
// Construct old.
etl::uninitialized_copy_n(p_end - construct_old_n, construct_old_n, p_construct_old, construct_count);
//*********************************************************************
/// Inserts a range of values to the vector.
/// If asserts or exceptions are enabled, emits vector_full if the vector does not have enough free space.
/// For fundamental and pointer types.
///\param position The position to insert before.
///\param first The first element to add.
///\param last The last + 1 element to add.
//*********************************************************************
template <class TIterator, typename U = T>
typename etl::enable_if<is_simple_type<U>::value, void>::type
insert(iterator position, TIterator first, TIterator last)
{
size_t count = std::distance(first, last);
// Copy old.
etl::copy_n(p_buffer + insert_begin, copy_old_n, p_buffer + insert_end);
ETL_ASSERT((current_size + count) <= MAX_SIZE, ETL_ERROR(vector_full));
U* p_end = &p_buffer[current_size];
std::copy_backward(position, p_end, p_end + count);
std::copy(first, last, position);
}
//*********************************************************************
/// Inserts a range of values to the vector.
/// If asserts or exceptions are enabled, emits vector_full if the vector does not have enough free space.
/// For fundamental and pointer types.
///\param position The position to insert before.
///\param first The first element to add.
///\param last The last + 1 element to add.
//*********************************************************************
template <class TIterator, typename U = T>
typename etl::enable_if<!is_simple_type<U>::value, void>::type
insert(iterator position, TIterator first, TIterator last)
{
size_t count = std::distance(first, last);
ETL_ASSERT((current_size + count) <= MAX_SIZE, ETL_ERROR(vector_full));
size_t insert_n = count;
size_t insert_begin = std::distance(begin(), position);
size_t insert_end = insert_begin + insert_n;
size_t new_size = current_size + insert_n;
size_t old_move_n = new_size - insert_end;
size_t new_copy_n = insert_n;
// Move old.
if (old_move_n > 0)
{
size_t index = new_size;
U* from = &p_buffer[size() - 1];
U* to = &p_buffer[size() - 1 + insert_n];
while (index > insert_end)
{
if (index > size())
{
// Construct.
new (to--) U(*from--);
++construct_count;
}
else
{
// Copy
*to-- = *from--;
}
--index;
}
}
// Construct new.
etl::uninitialized_copy_n(first + copy_new_n, construct_new_n, p_end, construct_count);
// Copy new.
if (new_copy_n > 0)
{
size_t index = insert_begin;
U* begin = &p_buffer[insert_begin];
U* end = &p_buffer[insert_end];
etl::copy_n(first, copy_new_n, p_buffer + insert_begin);
#else
// Construct old.
etl::uninitialized_copy_n(p_end - construct_old_n, construct_old_n, p_construct_old);
while (begin != end)
{
if (index >= size())
{
// Construct.
new (begin++) T(*first++);
++construct_count;
}
else
{
// Copy.
*begin++ = *first++;
}
// Copy old.
etl::copy_n(p_buffer + insert_begin, copy_old_n, p_buffer + insert_end);
++index;
}
}
// Construct new.
etl::uninitialized_copy_n(first + copy_new_n, construct_new_n, p_end);
current_size += count;
// Copy new.
etl::copy_n(first, copy_new_n, p_buffer + insert_begin);
#endif
p_end += count;
}
//*********************************************************************
@ -742,7 +679,7 @@ namespace etl
iterator erase(iterator i_element)
{
std::copy(i_element + 1, end(), i_element);
destroy_element();
destroy_back();
return i_element;
}
@ -755,7 +692,36 @@ namespace etl
///\param last Iterator to the last element.
///\return An iterator pointing to the element that followed the erased element.
//*********************************************************************
iterator erase(iterator first, iterator last)
template <typename U = T>
typename etl::enable_if<etl::has_trivial_constructor<U>::value, iterator>::type
erase(iterator first, iterator last)
{
if (first == begin() && last == end())
{
clear();
}
else
{
std::copy(last, end(), first);
size_t n_delete = std::distance(first, last);
construct_count -= n_delete;
p_end -= n_delete;
}
return first;
}
//*********************************************************************
/// Erases a range of elements.
/// The range includes all the elements between first and last, including the
/// element pointed by first, but not the one pointed by last.
///\param first Iterator to the first element.
///\param last Iterator to the last element.
///\return An iterator pointing to the element that followed the erased element.
//*********************************************************************
template <typename U = T>
typename etl::enable_if<!etl::has_trivial_constructor<U>::value, iterator>::type
erase(iterator first, iterator last)
{
if (first == begin() && last == end())
{
@ -766,19 +732,13 @@ namespace etl
std::copy(last, end(), first);
size_t n_delete = std::distance(first, last);
if (is_simple_type<value_type>::value)
{
// Just adjust the count.
current_size -= n_delete;
}
else
{
// Destroy the elements left over at the end.
while (n_delete-- > 0)
{
destroy_element();
}
}
// Destroy the elements left over at the end.
#if defined(ETL_DEBUG)
etl::destroy(p_end - n_delete, p_end, construct_count);
#else
etl::destroy(p_end - n_delete, p_end);
#endif
p_end -= n_delete;
}
return first;
@ -797,14 +757,51 @@ namespace etl
return *this;
}
//*************************************************************************
/// Gets the current size of the vector.
///\return The current size of the vector.
//*************************************************************************
size_type size() const
{
return size_t(p_end - p_buffer);
}
//*************************************************************************
/// Checks the 'empty' state of the vector.
///\return <b>true</b> if empty.
//*************************************************************************
bool empty() const
{
return (p_end == p_buffer);
}
//*************************************************************************
/// Checks the 'full' state of the vector.
///\return <b>true</b> if full.
//*************************************************************************
bool full() const
{
return size() == MAX_SIZE;
}
//*************************************************************************
/// Returns the remaining capacity.
///\return The remaining capacity.
//*************************************************************************
size_t available() const
{
return max_size() - size();
}
protected:
//*********************************************************************
/// Constructor.
//*********************************************************************
ivector(T* p_buffer, size_t MAX_SIZE)
ivector(T* p_buffer_, size_t MAX_SIZE)
: vector_base(MAX_SIZE),
p_buffer(p_buffer)
p_buffer(p_buffer_),
p_end(p_buffer_)
{
}
@ -813,87 +810,58 @@ namespace etl
//*********************************************************************
void initialise()
{
// Choose the algorithm according to type.
// The unused branch should be optimised away.
if (is_simple_type<value_type>::value)
{
current_size = 0;
}
else
{
while (current_size > 0)
{
destroy_element();
}
}
}
#if defined(ETL_DEBUG)
etl::destroy(p_buffer, p_end, construct_count);
#else
etl::destroy(p_buffer, p_end);
#endif
T* p_buffer;
p_end = p_buffer;
}
private:
pointer p_buffer; ///< Pointer to the start of the buffer.
pointer p_end; ///< Pointer to one past the last element in the buffer.
//*********************************************************************
/// Create a new element with a default value at the back.
//*********************************************************************
inline void create_element()
inline void create_back()
{
if (is_simple_type<T>::value)
{
current_size++;
}
else
{
new (&p_buffer[current_size++]) T();
++construct_count;
}
#if defined(ETL_DEBUG)
etl::create_value_at(p_end, construct_count);
#else
etl::create_value_at(p_end);
#endif
++p_end;
}
//*********************************************************************
/// Create a new element with a value at the back
//*********************************************************************
inline void create_element(parameter_t value)
inline void create_back(parameter_t value)
{
if (is_simple_type<T>::value)
{
p_buffer[current_size++] = value;
}
else
{
new (&p_buffer[current_size++]) T(value);
++construct_count;
}
}
//*********************************************************************
/// Create a new element with a value at the index
//*********************************************************************
inline void create_element_at(size_t index, parameter_t value)
{
new (&p_buffer[index]) T(value);
++construct_count;
#if defined(ETL_DEBUG)
etl::create_copy_at(p_end, value, construct_count);
#else
etl::create_copy_at(p_end, value);
#endif
++p_end;
}
//*********************************************************************
/// Destroy an element at the back.
//*********************************************************************
template <typename U = T>
typename etl::enable_if<is_simple_type<U>::value, void>::type
destroy_element()
inline void destroy_back()
{
--current_size;
}
--p_end;
//*********************************************************************
/// Destroy an element at the back.
//*********************************************************************
template <typename U = T>
typename etl::enable_if<!is_simple_type<U>::value, void>::type
destroy_element()
{
p_buffer[--current_size].~U();
--construct_count;
#if defined(ETL_DEBUG)
etl::destroy_at(p_end, construct_count);
#else
etl::destroy_at(p_end);
#endif
}
// Disable copy construction.

View File

@ -312,7 +312,7 @@ namespace etl
while (first != last)
{
p_buffer[current_size++] = (void*)*first++;
*p_end++ = (void*)*first++;
}
}