From ba7992f8889a384c1d0a81ad32757a2aed406f28 Mon Sep 17 00:00:00 2001 From: Wolfgang Bangerth Date: Mon, 2 Oct 2017 19:02:21 -0600 Subject: [PATCH] Allow using objects in Table and AlignedVector that are not copyable. --- include/deal.II/base/aligned_vector.h | 169 +++++++++++++++++++++++++- include/deal.II/base/table.h | 16 +-- 2 files changed, 171 insertions(+), 14 deletions(-) diff --git a/include/deal.II/base/aligned_vector.h b/include/deal.II/base/aligned_vector.h index 82479be127..fc54b7bc16 100644 --- a/include/deal.II/base/aligned_vector.h +++ b/include/deal.II/base/aligned_vector.h @@ -131,14 +131,35 @@ public: /** * Change the size of the vector. It keeps old elements previously - * available, and initializes each element with the specified data. If the - * new vector size is shorter than the old one, the memory is not released - * unless the new size is zero. + * available, and initializes each newly added element to a + * default-constructed object of type @p T. + * + * If the new vector size is shorter than the old one, the memory is + * not immediately released unless the new size is zero; however, + * the size of the current object is of course set to the requested + * value. + * + * @dealiiOperationIsMultithreaded + */ + void resize (const size_type size_in); + + /** + * Change the size of the vector. It keeps old elements previously + * available, and initializes each newly added element with the + * provided initializer. + * + * If the new vector size is shorter than the old one, the memory is + * not immediately released unless the new size is zero; however, + * the size of the current object is of course set to the requested + * value. + * + * @note This method can only be invoked for classes that define the copy + * assignment operator. Otherwise, compilation will fail. * * @dealiiOperationIsMultithreaded */ void resize (const size_type size_in, - const T &init = T()); + const T &init); /** * Reserve memory space for @p size elements. If the argument @p size is set @@ -181,6 +202,17 @@ public: void insert_back (ForwardIterator begin, ForwardIterator end); + /** + * Fills the vector with size() copies of a default constructed object. + * + * @note Unlike the other fill() function, this method can also be + * invoked for classes that do not define a copy assignment + * operator. + * + * @dealiiOperationIsMultithreaded + */ + void fill (); + /** * Fills the vector with size() copies of the given input. * @@ -363,7 +395,7 @@ namespace internal { // initialize memory (copy construct by placement new), and // destruct the source - new (&destination_[i]) T(source_[i]); + new (&destination_[i]) T(std::move(source_[i])); source_[i].~T(); } else @@ -468,6 +500,97 @@ namespace internal } }; + + + /** + * Class that issues the set commands for AlignedVector. + * + * @tparam initialize_memory Sets whether the set command should + * initialize memory (with a call to the copy constructor) or rather use the + * copy assignment operator. A template is necessary to select the + * appropriate operation since some classes might define only one of those + * two operations. + * + * @relates AlignedVector + */ + template + class AlignedVectorDefaultInitialize : private parallel::ParallelForInteger + { + static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1; + public: + /** + * Constructor. Issues a parallel call if there are sufficiently many + * elements, otherwise work in serial. + */ + AlignedVectorDefaultInitialize (const std::size_t size, + T *destination) + : + destination_ (destination), + trivial_element (false) + { + if (size == 0) + return; + + // do not use memcmp for long double because on some systems it does not + // completely fill its memory and may lead to false positives in + // e.g. valgrind + if (std::is_trivial::value == true && + std::is_same::value == false) + { + const unsigned char zero [sizeof(T)] = {}; + // cast element to (void*) to silence compiler warning for virtual + // classes (they will never arrive here because they are + // non-trivial). + T element {}; + if (std::memcmp(zero, (void *)&element, sizeof(T)) == 0) + trivial_element = true; + } + if (size < minimum_parallel_grain_size) + apply_to_subrange (0, size); + else + apply_parallel (0, size, minimum_parallel_grain_size); + } + + /** + * This initializes elements on a subrange given by two integers. + */ + virtual void apply_to_subrange (const std::size_t begin, + const std::size_t end) const + { + // for classes with trivial assignment of zero can use memset. cast + // element to (void*) to silence compiler warning for virtual + // classes (they will never arrive here because they are + // non-trivial). + if (std::is_trivial::value == true && trivial_element) + std::memset ((void *)(destination_+begin), 0, (end-begin)*sizeof(T)); + else + default_construct_or_assign(begin, end, + std::integral_constant()); + } + + private: + mutable T *destination_; + bool trivial_element; + + // copy assignment operation + void default_construct_or_assign(const std::size_t begin, + const std::size_t end, + std::integral_constant) const + { + for (std::size_t i=begin; i) const + { + for (std::size_t i=begin; i::resize_fast (const size_type size_in) // need to still set the values in case the class is non-trivial because // virtual classes etc. need to run their (default) constructor if (std::is_trivial::value == false && size_in > old_size) - dealii::internal::AlignedVectorSet (size_in-old_size, T(), _data+old_size); + dealii::internal::AlignedVectorDefaultInitialize (size_in-old_size, _data+old_size); +} + + + +template < class T > +inline +void +AlignedVector::resize (const size_type size_in) +{ + const size_type old_size = size(); + if (std::is_trivial::value == false && size_in < old_size) + { + // call destructor on fields that are released. doing it backward + // releases the elements in reverse order as compared to how they were + // created + while (_end_data != _data+size_in) + (--_end_data)->~T(); + } + reserve (size_in); + _end_data = _data + size_in; + + // finally set the desired init values + if (size_in > old_size) + dealii::internal::AlignedVectorDefaultInitialize (size_in-old_size, _data+old_size); } @@ -745,6 +892,16 @@ AlignedVector::insert_back (ForwardIterator begin, +template < class T > +inline +void +AlignedVector::fill () +{ + dealii::internal::AlignedVectorDefaultInitialize (size(), _data); +} + + + template < class T > inline void diff --git a/include/deal.II/base/table.h b/include/deal.II/base/table.h index cfebb63c63..8cfc6ba8dc 100644 --- a/include/deal.II/base/table.h +++ b/include/deal.II/base/table.h @@ -492,12 +492,12 @@ public: void reset_values (); /** - * Set the dimensions of this object to the sizes given in the argument, and - * newly allocate the required memory. If - * omit_default_initialization is set to false, all - * elements of the table are set to a default constructed object for the - * element type. Otherwise the memory is left in an uninitialized or - * otherwise undefined state. + * Set the dimensions of this object to the sizes given in the first + * argument, and allocate the required memory for table entries to + * accommodate these sizes. If @p omit_default_initialization + * is set to @p false, all elements of the table are set to a + * default constructed object for the element type. Otherwise the + * memory is left in an uninitialized or otherwise undefined state. */ void reinit (const TableIndices &new_size, const bool omit_default_initialization = false); @@ -1908,11 +1908,11 @@ TableBase::reinit (const TableIndices &new_sizes, if (!omit_default_initialization) { if (values.empty()) - values.resize(new_size, T()); + values.resize(new_size); else { values.resize_fast(new_size); - values.fill(T()); + values.fill(); } } else -- 2.39.5