Reference documentation for deal.II version 9.1.0-pre
aligned_vector.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2011 - 2018 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 #ifndef dealii_aligned_vector_h
18 #define dealii_aligned_vector_h
19 
20 #include <deal.II/base/config.h>
21 
22 #include <deal.II/base/exceptions.h>
23 #include <deal.II/base/memory_consumption.h>
24 #include <deal.II/base/parallel.h>
25 #include <deal.II/base/utilities.h>
26 
27 // boost::serialization::make_array used to be in array.hpp, but was
28 // moved to a different file in BOOST 1.64
29 #include <boost/version.hpp>
30 #if BOOST_VERSION >= 106400
31 # include <boost/serialization/array_wrapper.hpp>
32 #else
33 # include <boost/serialization/array.hpp>
34 #endif
35 #include <boost/serialization/split_member.hpp>
36 
37 #include <cstring>
38 #include <type_traits>
39 
40 
41 
42 DEAL_II_NAMESPACE_OPEN
43 
44 
60 template <class T>
62 {
63 public:
68  using value_type = T;
69  using pointer = value_type *;
70  using const_pointer = const value_type *;
71  using iterator = value_type *;
72  using const_iterator = const value_type *;
73  using reference = value_type &;
74  using const_reference = const value_type &;
75  using size_type = std::size_t;
76 
80  AlignedVector();
81 
88  AlignedVector(const size_type size, const T &init = T());
89 
94 
100  AlignedVector(const AlignedVector<T> &vec);
101 
106  AlignedVector(AlignedVector<T> &&vec) noexcept;
107 
113  AlignedVector &
114  operator=(const AlignedVector<T> &vec);
115 
119  AlignedVector &
120  operator=(AlignedVector<T> &&vec) noexcept;
121 
130  void
131  resize_fast(const size_type size);
132 
145  void
146  resize(const size_type size_in);
147 
163  void
164  resize(const size_type size_in, const T &init);
165 
174  void
175  reserve(const size_type size_alloc);
176 
181  void
182  clear();
183 
189  void
190  push_back(const T in_data);
191 
195  reference
196  back();
197 
202  back() const;
203 
208  template <typename ForwardIterator>
209  void
210  insert_back(ForwardIterator begin, ForwardIterator end);
211 
221  void
222  fill();
223 
232  void
233  fill(const T &element);
234 
238  void
239  swap(AlignedVector<T> &vec);
240 
244  bool
245  empty() const;
246 
250  size_type
251  size() const;
252 
257  size_type
258  capacity() const;
259 
263  reference operator[](const size_type index);
264 
268  const_reference operator[](const size_type index) const;
269 
273  iterator
274  begin();
275 
279  iterator
280  end();
281 
286  begin() const;
287 
292  end() const;
293 
299  size_type
300  memory_consumption() const;
301 
306  template <class Archive>
307  void
308  save(Archive &ar, const unsigned int version) const;
309 
314  template <class Archive>
315  void
316  load(Archive &ar, const unsigned int version);
317 
318  BOOST_SERIALIZATION_SPLIT_MEMBER()
319 
320 private:
324  T *_data;
325 
330 
335 };
336 
337 
338 // ------------------------------- inline functions --------------------------
339 
345 namespace internal
346 {
362  template <typename T>
364  {
365  static const std::size_t minimum_parallel_grain_size =
366  160000 / sizeof(T) + 1;
367 
368  public:
378  AlignedVectorCopy(const T *const source_begin,
379  const T *const source_end,
380  T *const destination)
381  : source_(source_begin)
382  , destination_(destination)
383  {
384  Assert(source_end >= source_begin, ExcInternalError());
385  Assert(source_end == source_begin || destination != nullptr,
386  ExcInternalError());
387  const std::size_t size = source_end - source_begin;
388  if (size < minimum_parallel_grain_size)
389  apply_to_subrange(0, size);
390  else
391  apply_parallel(0, size, minimum_parallel_grain_size);
392  }
393 
398  virtual void
399  apply_to_subrange(const std::size_t begin,
400  const std::size_t end) const override
401  {
402  if (end == begin)
403  return;
404 
405  // for classes trivial assignment can use memcpy. cast element to
406  // (void*) to silence compiler warning for virtual classes (they will
407  // never arrive here because they are non-trivial).
408 
409  if (std::is_trivial<T>::value == true)
410  std::memcpy((void *)(destination_ + begin),
411  (void *)(source_ + begin),
412  (end - begin) * sizeof(T));
413  else
414  for (std::size_t i = begin; i < end; ++i)
415  new (&destination_[i]) T(source_[i]);
416  }
417 
418  private:
419  const T *const source_;
420  T *const destination_;
421  };
422 
423 
429  template <typename T>
431  {
432  static const std::size_t minimum_parallel_grain_size =
433  160000 / sizeof(T) + 1;
434 
435  public:
445  AlignedVectorMove(T *const source_begin,
446  T *const source_end,
447  T *const destination)
448  : source_(source_begin)
449  , destination_(destination)
450  {
451  Assert(source_end >= source_begin, ExcInternalError());
452  Assert(source_end == source_begin || destination != nullptr,
453  ExcInternalError());
454  const std::size_t size = source_end - source_begin;
455  if (size < minimum_parallel_grain_size)
456  apply_to_subrange(0, size);
457  else
458  apply_parallel(0, size, minimum_parallel_grain_size);
459  }
460 
465  virtual void
466  apply_to_subrange(const std::size_t begin,
467  const std::size_t end) const override
468  {
469  if (end == begin)
470  return;
471 
472  // for classes trivial assignment can use memcpy. cast element to
473  // (void*) to silence compiler warning for virtual classes (they will
474  // never arrive here because they are non-trivial).
475 
476  if (std::is_trivial<T>::value == true)
477  std::memcpy((void *)(destination_ + begin),
478  (void *)(source_ + begin),
479  (end - begin) * sizeof(T));
480  else
481  for (std::size_t i = begin; i < end; ++i)
482  {
483  // initialize memory (copy construct by placement new), and
484  // destruct the source
485  new (&destination_[i]) T(std::move(source_[i]));
486  source_[i].~T();
487  }
488  }
489 
490  private:
491  T *const source_;
492  T *const destination_;
493  };
494 
495 
507  template <typename T, bool initialize_memory>
509  {
510  static const std::size_t minimum_parallel_grain_size =
511  160000 / sizeof(T) + 1;
512 
513  public:
518  AlignedVectorSet(const std::size_t size,
519  const T & element,
520  T *const destination)
521  : element_(element)
522  , destination_(destination)
523  , trivial_element(false)
524  {
525  if (size == 0)
526  return;
527  Assert(destination != nullptr, ExcInternalError());
528 
529  // do not use memcmp for long double because on some systems it does not
530  // completely fill its memory and may lead to false positives in
531  // e.g. valgrind
532  if (std::is_trivial<T>::value == true &&
533  std::is_same<T, long double>::value == false)
534  {
535  const unsigned char zero[sizeof(T)] = {};
536  // cast element to (void*) to silence compiler warning for virtual
537  // classes (they will never arrive here because they are
538  // non-trivial).
539  if (std::memcmp(zero, (void *)&element, sizeof(T)) == 0)
540  trivial_element = true;
541  }
542  if (size < minimum_parallel_grain_size)
543  apply_to_subrange(0, size);
544  else
545  apply_parallel(0, size, minimum_parallel_grain_size);
546  }
547 
551  virtual void
552  apply_to_subrange(const std::size_t begin,
553  const std::size_t end) const override
554  {
555  // for classes with trivial assignment of zero can use memset. cast
556  // element to (void*) to silence compiler warning for virtual
557  // classes (they will never arrive here because they are
558  // non-trivial).
559  if (std::is_trivial<T>::value == true && trivial_element)
560  std::memset((void *)(destination_ + begin),
561  0,
562  (end - begin) * sizeof(T));
563  else
564  copy_construct_or_assign(
565  begin, end, std::integral_constant<bool, initialize_memory>());
566  }
567 
568  private:
569  const T & element_;
570  mutable T *destination_;
571  bool trivial_element;
572 
573  // copy assignment operation
574  void
575  copy_construct_or_assign(const std::size_t begin,
576  const std::size_t end,
577  std::integral_constant<bool, false>) const
578  {
579  for (std::size_t i = begin; i < end; ++i)
580  destination_[i] = element_;
581  }
582 
583  // copy constructor (memory initialization)
584  void
585  copy_construct_or_assign(const std::size_t begin,
586  const std::size_t end,
587  std::integral_constant<bool, true>) const
588  {
589  for (std::size_t i = begin; i < end; ++i)
590  new (&destination_[i]) T(element_);
591  }
592  };
593 
594 
595 
607  template <typename T, bool initialize_memory>
609  {
610  static const std::size_t minimum_parallel_grain_size =
611  160000 / sizeof(T) + 1;
612 
613  public:
618  AlignedVectorDefaultInitialize(const std::size_t size, T *const destination)
619  : destination_(destination)
620  {
621  if (size == 0)
622  return;
623  Assert(destination != nullptr, ExcInternalError());
624 
625  if (size < minimum_parallel_grain_size)
626  apply_to_subrange(0, size);
627  else
628  apply_parallel(0, size, minimum_parallel_grain_size);
629  }
630 
634  virtual void
635  apply_to_subrange(const std::size_t begin,
636  const std::size_t end) const override
637  {
638  // for classes with trivial assignment of zero can use memset. cast
639  // element to (void*) to silence compiler warning for virtual
640  // classes (they will never arrive here because they are
641  // non-trivial).
642  if (std::is_trivial<T>::value == true)
643  std::memset((void *)(destination_ + begin),
644  0,
645  (end - begin) * sizeof(T));
646  else
647  default_construct_or_assign(
648  begin, end, std::integral_constant<bool, initialize_memory>());
649  }
650 
651  private:
652  mutable T *destination_;
653 
654  // copy assignment operation
655  void
656  default_construct_or_assign(const std::size_t begin,
657  const std::size_t end,
658  std::integral_constant<bool, false>) const
659  {
660  for (std::size_t i = begin; i < end; ++i)
661  destination_[i] = std::move(T());
662  }
663 
664  // copy constructor (memory initialization)
665  void
666  default_construct_or_assign(const std::size_t begin,
667  const std::size_t end,
668  std::integral_constant<bool, true>) const
669  {
670  for (std::size_t i = begin; i < end; ++i)
671  new (&destination_[i]) T;
672  }
673  };
674 
675 } // end of namespace internal
676 
677 
678 #ifndef DOXYGEN
679 
680 
681 template <class T>
683  : _data(nullptr)
684  , _end_data(nullptr)
685  , _end_allocated(nullptr)
686 {}
687 
688 
689 
690 template <class T>
691 inline AlignedVector<T>::AlignedVector(const size_type size, const T &init)
692  : _data(nullptr)
693  , _end_data(nullptr)
694  , _end_allocated(nullptr)
695 {
696  if (size > 0)
697  resize(size, init);
698 }
699 
700 
701 
702 template <class T>
704 {
705  clear();
706 }
707 
708 
709 
710 template <class T>
712  : _data(nullptr)
713  , _end_data(nullptr)
714  , _end_allocated(nullptr)
715 {
716  // copy the data from vec
717  reserve(vec._end_data - vec._data);
720 }
721 
722 
723 
724 template <class T>
726  : _data(vec._data)
727  , _end_data(vec._end_data)
729 {
730  vec._data = nullptr;
731  vec._end_data = nullptr;
732  vec._end_allocated = nullptr;
733 }
734 
735 
736 
737 template <class T>
738 inline AlignedVector<T> &
740 {
741  resize(0);
742  resize_fast(vec._end_data - vec._data);
744  return *this;
745 }
746 
747 
748 
749 template <class T>
750 inline AlignedVector<T> &
752 {
753  clear();
754 
755  _data = vec._data;
756  _end_data = vec._end_data;
758 
759  vec._data = nullptr;
760  vec._end_data = nullptr;
761  vec._end_allocated = nullptr;
762 
763  return *this;
764 }
765 
766 
767 
768 template <class T>
769 inline void
770 AlignedVector<T>::resize_fast(const size_type size_in)
771 {
772  const size_type old_size = size();
773  if (std::is_trivial<T>::value == false && size_in < old_size)
774  {
775  // call destructor on fields that are released. doing it backward
776  // releases the elements in reverse order as compared to how they were
777  // created
778  while (_end_data != _data + size_in)
779  (--_end_data)->~T();
780  }
781  reserve(size_in);
782  _end_data = _data + size_in;
783 
784  // need to still set the values in case the class is non-trivial because
785  // virtual classes etc. need to run their (default) constructor
786  if (std::is_trivial<T>::value == false && size_in > old_size)
788  old_size,
789  _data + old_size);
790 }
791 
792 
793 
794 template <class T>
795 inline void
796 AlignedVector<T>::resize(const size_type size_in)
797 {
798  const size_type old_size = size();
799  if (std::is_trivial<T>::value == false && size_in < old_size)
800  {
801  // call destructor on fields that are released. doing it backward
802  // releases the elements in reverse order as compared to how they were
803  // created
804  while (_end_data != _data + size_in)
805  (--_end_data)->~T();
806  }
807  reserve(size_in);
808  _end_data = _data + size_in;
809 
810  // finally set the desired init values
811  if (size_in > old_size)
813  old_size,
814  _data + old_size);
815 }
816 
817 
818 
819 template <class T>
820 inline void
821 AlignedVector<T>::resize(const size_type size_in, const T &init)
822 {
823  const size_type old_size = size();
824  if (std::is_trivial<T>::value == false && size_in < old_size)
825  {
826  // call destructor on fields that are released. doing it backward
827  // releases the elements in reverse order as compared to how they were
828  // created
829  while (_end_data != _data + size_in)
830  (--_end_data)->~T();
831  }
832  reserve(size_in);
833  _end_data = _data + size_in;
834 
835  // finally set the desired init values
836  if (size_in > old_size)
837  ::internal::AlignedVectorSet<T, true>(size_in - old_size,
838  init,
839  _data + old_size);
840 }
841 
842 
843 
844 template <class T>
845 inline void
846 AlignedVector<T>::reserve(const size_type size_alloc)
847 {
848  const size_type old_size = _end_data - _data;
849  const size_type allocated_size = _end_allocated - _data;
850  if (size_alloc > allocated_size)
851  {
852  // if we continuously increase the size of the vector, we might be
853  // reallocating a lot of times. therefore, try to increase the size more
854  // aggressively
855  size_type new_size = size_alloc;
856  if (size_alloc < (2 * allocated_size))
857  new_size = 2 * allocated_size;
858 
859  const size_type size_actual_allocate = new_size * sizeof(T);
860 
861  // allocate and align along 64-byte boundaries (this is enough for all
862  // levels of vectorization currently supported by deal.II)
863  T *new_data;
864  Utilities::System::posix_memalign((void **)&new_data,
865  64,
866  size_actual_allocate);
867 
868  // copy data in case there was some content before and release the old
869  // memory with the function corresponding to the one used for allocating
870  std::swap(_data, new_data);
871  _end_data = _data + old_size;
872  _end_allocated = _data + new_size;
873  if (_end_data != _data)
874  {
876  new_data + old_size,
877  _data);
878  free(new_data);
879  }
880  else
881  Assert(new_data == nullptr, ExcInternalError());
882  }
883  else if (size_alloc == 0)
884  clear();
885 }
886 
887 
888 
889 template <class T>
890 inline void
892 {
893  if (_data != nullptr)
894  {
895  if (std::is_trivial<T>::value == false)
896  while (_end_data != _data)
897  (--_end_data)->~T();
898 
899  free(_data);
900  }
901  _data = nullptr;
902  _end_data = nullptr;
903  _end_allocated = nullptr;
904 }
905 
906 
907 
908 template <class T>
909 inline void
910 AlignedVector<T>::push_back(const T in_data)
911 {
913  if (_end_data == _end_allocated)
914  reserve(std::max(2 * capacity(), static_cast<size_type>(16)));
915  if (std::is_trivial<T>::value == false)
916  new (_end_data++) T(in_data);
917  else
918  *_end_data++ = in_data;
919 }
920 
921 
922 
923 template <class T>
924 inline typename AlignedVector<T>::reference
926 {
927  AssertIndexRange(0, size());
928  T *field = _end_data - 1;
929  return *field;
930 }
931 
932 
933 
934 template <class T>
935 inline typename AlignedVector<T>::const_reference
937 {
938  AssertIndexRange(0, size());
939  const T *field = _end_data - 1;
940  return *field;
941 }
942 
943 
944 
945 template <class T>
946 template <typename ForwardIterator>
947 inline void
948 AlignedVector<T>::insert_back(ForwardIterator begin, ForwardIterator end)
949 {
950  const unsigned int old_size = size();
951  reserve(old_size + (end - begin));
952  for (; begin != end; ++begin, ++_end_data)
953  {
954  if (std::is_trivial<T>::value == false)
955  new (_end_data) T;
956  *_end_data = *begin;
957  }
958 }
959 
960 
961 
962 template <class T>
963 inline void
965 {
967 }
968 
969 
970 
971 template <class T>
972 inline void
973 AlignedVector<T>::fill(const T &value)
974 {
976 }
977 
978 
979 
980 template <class T>
981 inline void
983 {
984  std::swap(_data, vec._data);
987 }
988 
989 
990 
991 template <class T>
992 inline bool
994 {
995  return _end_data == _data;
996 }
997 
998 
999 
1000 template <class T>
1001 inline typename AlignedVector<T>::size_type
1002 AlignedVector<T>::size() const
1003 {
1004  return _end_data - _data;
1005 }
1006 
1007 
1008 
1009 template <class T>
1010 inline typename AlignedVector<T>::size_type
1012 {
1013  return _end_allocated - _data;
1014 }
1015 
1016 
1017 
1018 template <class T>
1019 inline typename AlignedVector<T>::reference AlignedVector<T>::
1020  operator[](const size_type index)
1021 {
1022  AssertIndexRange(index, size());
1023  return _data[index];
1024 }
1025 
1026 
1027 
1028 template <class T>
1029 inline typename AlignedVector<T>::const_reference AlignedVector<T>::
1030  operator[](const size_type index) const
1031 {
1032  AssertIndexRange(index, size());
1033  return _data[index];
1034 }
1035 
1036 
1037 
1038 template <class T>
1039 inline typename AlignedVector<T>::iterator
1041 {
1042  return _data;
1043 }
1044 
1045 
1046 
1047 template <class T>
1048 inline typename AlignedVector<T>::iterator
1050 {
1051  return _end_data;
1052 }
1053 
1054 
1055 
1056 template <class T>
1057 inline typename AlignedVector<T>::const_iterator
1059 {
1060  return _data;
1061 }
1062 
1063 
1064 
1065 template <class T>
1066 inline typename AlignedVector<T>::const_iterator
1067 AlignedVector<T>::end() const
1068 {
1069  return _end_data;
1070 }
1071 
1072 
1073 
1074 template <class T>
1075 template <class Archive>
1076 inline void
1077 AlignedVector<T>::save(Archive &ar, const unsigned int) const
1078 {
1079  size_type vec_size(size());
1080  ar & vec_size;
1081  if (vec_size > 0)
1082  ar &boost::serialization::make_array(_data, vec_size);
1083 }
1084 
1085 
1086 
1087 template <class T>
1088 template <class Archive>
1089 inline void
1090 AlignedVector<T>::load(Archive &ar, const unsigned int)
1091 {
1092  size_type vec_size = 0;
1093  ar & vec_size;
1094 
1095  if (vec_size > 0)
1096  {
1097  reserve(vec_size);
1098  ar &boost::serialization::make_array(_data, vec_size);
1099  _end_data = _data + vec_size;
1100  }
1101 }
1102 
1103 
1104 
1105 template <class T>
1106 inline typename AlignedVector<T>::size_type
1108 {
1109  size_type memory = sizeof(*this);
1110  for (const T *t = _data; t != _end_data; ++t)
1111  memory += ::MemoryConsumption::memory_consumption(*t);
1112  memory += sizeof(T) * (_end_allocated - _end_data);
1113  return memory;
1114 }
1115 
1116 
1117 #endif // ifndef DOXYGEN
1118 
1119 
1125 template <class T>
1126 bool
1128 {
1129  if (lhs.size() != rhs.size())
1130  return false;
1131  for (typename AlignedVector<T>::const_iterator lit = lhs.begin(),
1132  rit = rhs.begin();
1133  lit != lhs.end();
1134  ++lit, ++rit)
1135  if (*lit != *rit)
1136  return false;
1137  return true;
1138 }
1139 
1140 
1141 
1147 template <class T>
1148 bool
1150 {
1151  return !(operator==(lhs, rhs));
1152 }
1153 
1154 
1155 DEAL_II_NAMESPACE_CLOSE
1156 
1157 #endif
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const override
size_type capacity() const
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const override
#define AssertIndexRange(index, range)
Definition: exceptions.h:1407
size_type memory_consumption() const
void load(Archive &ar, const unsigned int version)
AlignedVector & operator=(const AlignedVector< T > &vec)
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const override
void reserve(const size_type size_alloc)
void push_back(const T in_data)
void resize(const size_type size_in)
reference operator[](const size_type index)
void save(Archive &ar, const unsigned int version) const
size_type size() const
bool operator==(const AlignedVector< T > &lhs, const AlignedVector< T > &rhs)
#define Assert(cond, exc)
Definition: exceptions.h:1227
void posix_memalign(void **memptr, size_t alignment, size_t size)
Definition: utilities.cc:732
void insert_back(ForwardIterator begin, ForwardIterator end)
void swap(Vector< Number > &u, Vector< Number > &v)
Definition: vector.h:1353
void swap(AlignedVector< T > &vec)
void resize_fast(const size_type size)
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const override
iterator end()
AlignedVectorMove(T *const source_begin, T *const source_end, T *const destination)
AlignedVectorCopy(const T *const source_begin, const T *const source_end, T *const destination)
iterator begin()
AlignedVectorSet(const std::size_t size, const T &element, T *const destination)
bool operator!=(const AlignedVector< T > &lhs, const AlignedVector< T > &rhs)
AlignedVectorDefaultInitialize(const std::size_t size, T *const destination)
bool empty() const
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
reference back()
static::ExceptionBase & ExcInternalError()