From 46a11d1747a902322f661340c4b647cbf2548b78 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 23 Feb 2017 23:14:58 +0000 Subject: [PATCH] Performance enhancements --- src/ivector.h | 706 +++++++++++++++++------------------ src/private/ivectorpointer.h | 2 +- 2 files changed, 338 insertions(+), 370 deletions(-) diff --git a/src/ivector.h b/src/ivector.h index b18e7a77..056ebd7c 100644 --- a/src/ivector.h +++ b/src/ivector.h @@ -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::difference_type difference_type; - private: - - // Type trait to allow selection of simple algorithms for simple types. - template - struct is_simple_type : integral_constant::value || etl::is_pointer::value> - { - }; - protected: typedef typename parameter_type::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) - { - // 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 etl::enable_if::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) - { - // 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 etl::enable_if::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 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(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) - { - 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) - { - 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 etl::enable_if::value, void>::type + typename etl::enable_if::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 etl::enable_if::value, void>::type + typename etl::enable_if::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 + typename etl::enable_if::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 + typename etl::enable_if::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 - typename etl::enable_if::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 - typename etl::enable_if::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 etl::enable_if::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 etl::enable_if::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) - { - // 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 true if empty. + //************************************************************************* + bool empty() const + { + return (p_end == p_buffer); + } + + //************************************************************************* + /// Checks the 'full' state of the vector. + ///\return true 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) - { - 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::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::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 etl::enable_if::value, void>::type - destroy_element() + inline void destroy_back() { - --current_size; - } + --p_end; - //********************************************************************* - /// Destroy an element at the back. - //********************************************************************* - template - typename etl::enable_if::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. diff --git a/src/private/ivectorpointer.h b/src/private/ivectorpointer.h index 1aae4e5c..da1ae67b 100644 --- a/src/private/ivectorpointer.h +++ b/src/private/ivectorpointer.h @@ -312,7 +312,7 @@ namespace etl while (first != last) { - p_buffer[current_size++] = (void*)*first++; + *p_end++ = (void*)*first++; } }