Reference documentation for deal.II version 9.0.0
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 at
12 // the top level of the deal.II distribution.
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 #include <deal.II/base/exceptions.h>
22 #include <deal.II/base/memory_consumption.h>
23 #include <deal.II/base/utilities.h>
24 #include <deal.II/base/parallel.h>
25 
26 // boost::serialization::make_array used to be in array.hpp, but was
27 // moved to a different file in BOOST 1.64
28 #include <boost/version.hpp>
29 #if BOOST_VERSION >= 106400
30 # include <boost/serialization/array_wrapper.hpp>
31 #else
32 # include <boost/serialization/array.hpp>
33 #endif
34 #include <boost/serialization/split_member.hpp>
35 
36 #include <cstring>
37 #include <type_traits>
38 
39 
40 
41 DEAL_II_NAMESPACE_OPEN
42 
43 
59 template < class T >
61 {
62 public:
67  typedef T value_type;
68  typedef value_type *pointer;
69  typedef const value_type *const_pointer;
70  typedef value_type *iterator;
71  typedef const value_type *const_iterator;
72  typedef value_type &reference;
73  typedef const value_type &const_reference;
74  typedef std::size_t size_type;
75 
79  AlignedVector ();
80 
87  AlignedVector (const size_type size,
88  const T &init = T());
89 
93  ~AlignedVector ();
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 resize_fast (const size_type size);
131 
144  void resize (const size_type size_in);
145 
161  void resize (const size_type size_in,
162  const T &init);
163 
172  void reserve (const size_type size_alloc);
173 
178  void clear ();
179 
185  void push_back (const T in_data);
186 
190  reference back ();
191 
195  const_reference back () const;
196 
201  template <typename ForwardIterator>
202  void insert_back (ForwardIterator begin,
203  ForwardIterator end);
204 
214  void fill ();
215 
224  void fill (const T &element);
225 
229  void swap (AlignedVector<T> &vec);
230 
234  bool empty () const;
235 
239  size_type size () const;
240 
245  size_type capacity () const;
246 
250  reference
251  operator [] (const size_type index);
252 
256  const_reference operator [] (const size_type index) const;
257 
261  iterator begin ();
262 
266  iterator end ();
267 
271  const_iterator begin () const;
272 
276  const_iterator end () const;
277 
283  size_type memory_consumption () const;
284 
289  template <class Archive>
290  void save (Archive &ar, const unsigned int version) const;
291 
296  template <class Archive>
297  void load (Archive &ar, const unsigned int version);
298 
299  BOOST_SERIALIZATION_SPLIT_MEMBER()
300 
301 private:
302 
306  T *_data;
307 
312 
317 };
318 
319 
320 // ------------------------------- inline functions --------------------------
321 
327 namespace internal
328 {
344  template <typename T>
346  {
347  static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
348  public:
358  AlignedVectorCopy (const T *const source_begin,
359  const T *const source_end,
360  T *const destination)
361  :
362  source_ (source_begin),
363  destination_ (destination)
364  {
365  Assert (source_end >= source_begin, ExcInternalError());
366  Assert (source_end == source_begin || destination != nullptr, ExcInternalError());
367  const std::size_t size = source_end - source_begin;
368  if (size < minimum_parallel_grain_size)
369  apply_to_subrange (0, size);
370  else
371  apply_parallel (0, size, minimum_parallel_grain_size);
372  }
373 
378  virtual void apply_to_subrange (const std::size_t begin,
379  const std::size_t end) const
380  {
381  if (end == begin)
382  return;
383 
384  // for classes trivial assignment can use memcpy. cast element to
385  // (void*) to silence compiler warning for virtual classes (they will
386  // never arrive here because they are non-trivial).
387 
388  if (std::is_trivial<T>::value == true)
389  std::memcpy ((void *)(destination_+begin), (void *)(source_+begin),
390  (end-begin)*sizeof(T));
391  else
392  for (std::size_t i=begin; i<end; ++i)
393  new (&destination_[i]) T(source_[i]);
394  }
395 
396  private:
397  const T *const source_;
398  T *const destination_;
399  };
400 
401 
407  template <typename T>
409  {
410  static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
411  public:
421  AlignedVectorMove (T *const source_begin,
422  T *const source_end,
423  T *const destination)
424  :
425  source_ (source_begin),
426  destination_ (destination)
427  {
428  Assert (source_end >= source_begin, ExcInternalError());
429  Assert (source_end == source_begin || destination != nullptr, ExcInternalError());
430  const std::size_t size = source_end - source_begin;
431  if (size < minimum_parallel_grain_size)
432  apply_to_subrange (0, size);
433  else
434  apply_parallel (0, size, minimum_parallel_grain_size);
435  }
436 
441  virtual void apply_to_subrange (const std::size_t begin,
442  const std::size_t end) const
443  {
444  if (end == begin)
445  return;
446 
447  // for classes trivial assignment can use memcpy. cast element to
448  // (void*) to silence compiler warning for virtual classes (they will
449  // never arrive here because they are non-trivial).
450 
451  if (std::is_trivial<T>::value == true)
452  std::memcpy ((void *)(destination_+begin), (void *)(source_+begin),
453  (end-begin)*sizeof(T));
454  else
455  for (std::size_t i=begin; i<end; ++i)
456  {
457  // initialize memory (copy construct by placement new), and
458  // destruct the source
459  new (&destination_[i]) T(std::move(source_[i]));
460  source_[i].~T();
461  }
462  }
463 
464  private:
465  T *const source_;
466  T *const destination_;
467  };
468 
469 
481  template <typename T, bool initialize_memory>
483  {
484  static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
485  public:
490  AlignedVectorSet (const std::size_t size,
491  const T &element,
492  T *const destination)
493  :
494  element_ (element),
495  destination_ (destination),
496  trivial_element (false)
497  {
498  if (size == 0)
499  return;
500  Assert (destination != nullptr, ExcInternalError());
501 
502  // do not use memcmp for long double because on some systems it does not
503  // completely fill its memory and may lead to false positives in
504  // e.g. valgrind
505  if (std::is_trivial<T>::value == true &&
506  std::is_same<T,long double>::value == false)
507  {
508  const unsigned char zero [sizeof(T)] = {};
509  // cast element to (void*) to silence compiler warning for virtual
510  // classes (they will never arrive here because they are
511  // non-trivial).
512  if (std::memcmp(zero, (void *)&element, sizeof(T)) == 0)
513  trivial_element = true;
514  }
515  if (size < minimum_parallel_grain_size)
516  apply_to_subrange (0, size);
517  else
518  apply_parallel (0, size, minimum_parallel_grain_size);
519  }
520 
524  virtual void apply_to_subrange (const std::size_t begin,
525  const std::size_t end) const
526  {
527  // for classes with trivial assignment of zero can use memset. cast
528  // element to (void*) to silence compiler warning for virtual
529  // classes (they will never arrive here because they are
530  // non-trivial).
531  if (std::is_trivial<T>::value == true && trivial_element)
532  std::memset ((void *)(destination_+begin), 0, (end-begin)*sizeof(T));
533  else
534  copy_construct_or_assign(begin, end,
535  std::integral_constant<bool, initialize_memory>());
536  }
537 
538  private:
539  const T &element_;
540  mutable T *destination_;
541  bool trivial_element;
542 
543  // copy assignment operation
544  void copy_construct_or_assign(const std::size_t begin,
545  const std::size_t end,
546  std::integral_constant<bool, false>) const
547  {
548  for (std::size_t i=begin; i<end; ++i)
549  destination_[i] = element_;
550  }
551 
552  // copy constructor (memory initialization)
553  void copy_construct_or_assign(const std::size_t begin,
554  const std::size_t end,
555  std::integral_constant<bool, true>) const
556  {
557  for (std::size_t i=begin; i<end; ++i)
558  new (&destination_[i]) T(element_);
559  }
560  };
561 
562 
563 
575  template <typename T, bool initialize_memory>
577  {
578  static const std::size_t minimum_parallel_grain_size = 160000/sizeof(T)+1;
579  public:
585  T *const destination)
586  :
587  destination_ (destination)
588  {
589  if (size == 0)
590  return;
591  Assert (destination != nullptr, ExcInternalError());
592 
593  if (size < minimum_parallel_grain_size)
594  apply_to_subrange (0, size);
595  else
596  apply_parallel (0, size, minimum_parallel_grain_size);
597  }
598 
602  virtual void apply_to_subrange (const std::size_t begin,
603  const std::size_t end) const
604  {
605  // for classes with trivial assignment of zero can use memset. cast
606  // element to (void*) to silence compiler warning for virtual
607  // classes (they will never arrive here because they are
608  // non-trivial).
609  if (std::is_trivial<T>::value == true)
610  std::memset ((void *)(destination_+begin), 0, (end-begin)*sizeof(T));
611  else
612  default_construct_or_assign(begin, end,
613  std::integral_constant<bool, initialize_memory>());
614  }
615 
616  private:
617  mutable T *destination_;
618 
619  // copy assignment operation
620  void default_construct_or_assign(const std::size_t begin,
621  const std::size_t end,
622  std::integral_constant<bool, false>) const
623  {
624  for (std::size_t i=begin; i<end; ++i)
625  destination_[i] = std::move(T());
626  }
627 
628  // copy constructor (memory initialization)
629  void default_construct_or_assign(const std::size_t begin,
630  const std::size_t end,
631  std::integral_constant<bool, true>) const
632  {
633  for (std::size_t i=begin; i<end; ++i)
634  new (&destination_[i]) T;
635  }
636  };
637 
638 } // end of namespace internal
639 
640 
641 #ifndef DOXYGEN
642 
643 
644 template < class T >
645 inline
647  :
648  _data (nullptr),
649  _end_data (nullptr),
650  _end_allocated (nullptr)
651 {}
652 
653 
654 
655 template < class T >
656 inline
657 AlignedVector<T>::AlignedVector (const size_type size,
658  const T &init)
659  :
660  _data (nullptr),
661  _end_data (nullptr),
662  _end_allocated (nullptr)
663 {
664  if (size > 0)
665  resize (size, init);
666 }
667 
668 
669 
670 template < class T >
671 inline
673 {
674  clear();
675 }
676 
677 
678 
679 template < class T >
680 inline
682  :
683  _data (nullptr),
684  _end_data (nullptr),
685  _end_allocated (nullptr)
686 {
687  // copy the data from vec
688  reserve (vec._end_data - vec._data);
689  _end_data = _end_allocated;
691 }
692 
693 
694 
695 template < class T >
696 inline
698 :
699 _data (vec._data),
700  _end_data (vec._end_data),
701  _end_allocated (vec._end_allocated)
702 {
703  vec._data = nullptr;
704  vec._end_data = nullptr;
705  vec._end_allocated = nullptr;
706 }
707 
708 
709 
710 template < class T >
711 inline
714 {
715  resize(0);
716  resize_fast (vec._end_data - vec._data);
718  return *this;
719 }
720 
721 
722 
723 template < class T >
724 inline
727 {
728  clear();
729 
730  _data = vec._data;
731  _end_data = vec._end_data;
732  _end_allocated = vec._end_allocated;
733 
734  vec._data = nullptr;
735  vec._end_data = nullptr;
736  vec._end_allocated = nullptr;
737 
738  return *this;
739 }
740 
741 
742 
743 template < class T >
744 inline
745 void
746 AlignedVector<T>::resize_fast (const size_type size_in)
747 {
748  const size_type old_size = size();
749  if (std::is_trivial<T>::value == false && size_in < old_size)
750  {
751  // call destructor on fields that are released. doing it backward
752  // releases the elements in reverse order as compared to how they were
753  // created
754  while (_end_data != _data+size_in)
755  (--_end_data)->~T();
756  }
757  reserve (size_in);
758  _end_data = _data + size_in;
759 
760  // need to still set the values in case the class is non-trivial because
761  // virtual classes etc. need to run their (default) constructor
762  if (std::is_trivial<T>::value == false && size_in > old_size)
763  ::internal::AlignedVectorDefaultInitialize<T,true> (size_in-old_size, _data+old_size);
764 }
765 
766 
767 
768 template < class T >
769 inline
770 void
771 AlignedVector<T>::resize (const size_type size_in)
772 {
773  const size_type old_size = size();
774  if (std::is_trivial<T>::value == false && size_in < old_size)
775  {
776  // call destructor on fields that are released. doing it backward
777  // releases the elements in reverse order as compared to how they were
778  // created
779  while (_end_data != _data+size_in)
780  (--_end_data)->~T();
781  }
782  reserve (size_in);
783  _end_data = _data + size_in;
784 
785  // finally set the desired init values
786  if (size_in > old_size)
787  ::internal::AlignedVectorDefaultInitialize<T,true> (size_in-old_size, _data+old_size);
788 }
789 
790 
791 
792 template < class T >
793 inline
794 void
795 AlignedVector<T>::resize (const size_type size_in,
796  const T &init)
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)
812  ::internal::AlignedVectorSet<T,true> (size_in-old_size, init, _data+old_size);
813 }
814 
815 
816 
817 template < class T >
818 inline
819 void
820 AlignedVector<T>::reserve (const size_type size_alloc)
821 {
822  const size_type old_size = _end_data - _data;
823  const size_type allocated_size = _end_allocated - _data;
824  if (size_alloc > allocated_size)
825  {
826  // if we continuously increase the size of the vector, we might be
827  // reallocating a lot of times. therefore, try to increase the size more
828  // aggressively
829  size_type new_size = size_alloc;
830  if (size_alloc < (2 * allocated_size))
831  new_size = 2 * allocated_size;
832 
833  const size_type size_actual_allocate = new_size * sizeof(T);
834 
835  // allocate and align along 64-byte boundaries (this is enough for all
836  // levels of vectorization currently supported by deal.II)
837  T *new_data;
838  Utilities::System::posix_memalign ((void **)&new_data, 64, size_actual_allocate);
839 
840  // copy data in case there was some content before and release the old
841  // memory with the function corresponding to the one used for allocating
842  std::swap (_data, new_data);
843  _end_data = _data + old_size;
844  _end_allocated = _data + new_size;
845  if (_end_data != _data)
846  {
847  ::internal::AlignedVectorMove<T>(new_data, new_data + old_size,
848  _data);
849  free(new_data);
850  }
851  else
852  Assert(new_data == nullptr, ExcInternalError());
853  }
854  else if (size_alloc == 0)
855  clear();
856 }
857 
858 
859 
860 template < class T >
861 inline
862 void
864 {
865  if (_data != nullptr)
866  {
867  if (std::is_trivial<T>::value == false)
868  while (_end_data != _data)
869  (--_end_data)->~T();
870 
871  free(_data);
872  }
873  _data = nullptr;
874  _end_data = nullptr;
875  _end_allocated = nullptr;
876 }
877 
878 
879 
880 template < class T >
881 inline
882 void
883 AlignedVector<T>::push_back (const T in_data)
884 {
885  Assert (_end_data <= _end_allocated, ExcInternalError());
886  if (_end_data == _end_allocated)
887  reserve (std::max(2*capacity(),static_cast<size_type>(16)));
888  if (std::is_trivial<T>::value == false)
889  new (_end_data++) T(in_data);
890  else
891  *_end_data++ = in_data;
892 }
893 
894 
895 
896 template < class T >
897 inline
898 typename AlignedVector<T>::reference
900 {
901  AssertIndexRange (0, size());
902  T *field = _end_data - 1;
903  return *field;
904 }
905 
906 
907 
908 template < class T >
909 inline
910 typename AlignedVector<T>::const_reference
911 AlignedVector<T>::back () const
912 {
913  AssertIndexRange (0, size());
914  const T *field = _end_data - 1;
915  return *field;
916 }
917 
918 
919 
920 template < class T >
921 template <typename ForwardIterator>
922 inline
923 void
924 AlignedVector<T>::insert_back (ForwardIterator begin,
925  ForwardIterator end)
926 {
927  const unsigned int old_size = size();
928  reserve (old_size + (end-begin));
929  for ( ; begin != end; ++begin, ++_end_data)
930  {
931  if (std::is_trivial<T>::value == false)
932  new (_end_data) T;
933  *_end_data = *begin;
934  }
935 }
936 
937 
938 
939 template < class T >
940 inline
941 void
943 {
945 }
946 
947 
948 
949 template < class T >
950 inline
951 void
952 AlignedVector<T>::fill (const T &value)
953 {
954  ::internal::AlignedVectorSet<T,false> (size(), value, _data);
955 }
956 
957 
958 
959 template < class T >
960 inline
961 void
963 {
964  std::swap (_data, vec._data);
965  std::swap (_end_data, vec._end_data);
966  std::swap (_end_allocated, vec._end_allocated);
967 }
968 
969 
970 
971 template < class T >
972 inline
973 bool
975 {
976  return _end_data == _data;
977 }
978 
979 
980 
981 template < class T >
982 inline
983 typename AlignedVector<T>::size_type
984 AlignedVector<T>::size () const
985 {
986  return _end_data - _data;
987 }
988 
989 
990 
991 template < class T >
992 inline
993 typename AlignedVector<T>::size_type
995 {
996  return _end_allocated - _data;
997 }
998 
999 
1000 
1001 template < class T >
1002 inline
1003 typename AlignedVector<T>::reference
1004 AlignedVector<T>::operator [] (const size_type index)
1005 {
1006  AssertIndexRange (index, size());
1007  return _data[index];
1008 }
1009 
1010 
1011 
1012 template < class T >
1013 inline
1014 typename AlignedVector<T>::const_reference
1015 AlignedVector<T>::operator [] (const size_type index) const
1016 {
1017  AssertIndexRange (index, size());
1018  return _data[index];
1019 }
1020 
1021 
1022 
1023 template < class T >
1024 inline
1025 typename AlignedVector<T>::iterator
1027 {
1028  return _data;
1029 }
1030 
1031 
1032 
1033 template < class T >
1034 inline
1035 typename AlignedVector<T>::iterator
1037 {
1038  return _end_data;
1039 }
1040 
1041 
1042 
1043 template < class T >
1044 inline
1045 typename AlignedVector<T>::const_iterator
1046 AlignedVector<T>::begin () const
1047 {
1048  return _data;
1049 }
1050 
1051 
1052 
1053 template < class T >
1054 inline
1055 typename AlignedVector<T>::const_iterator
1056 AlignedVector<T>::end () const
1057 {
1058  return _end_data;
1059 }
1060 
1061 
1062 
1063 template < class T >
1064 template < class Archive >
1065 inline
1066 void
1067 AlignedVector<T>::save (Archive &ar, const unsigned int) const
1068 {
1069  size_type vec_size (size());
1070  ar &vec_size;
1071  if (vec_size > 0)
1072  ar &boost::serialization::make_array(_data, vec_size);
1073 }
1074 
1075 
1076 
1077 template < class T >
1078 template < class Archive >
1079 inline
1080 void
1081 AlignedVector<T>::load (Archive &ar, const unsigned int)
1082 {
1083  size_type vec_size = 0;
1084  ar &vec_size ;
1085 
1086  if (vec_size > 0)
1087  {
1088  reserve(vec_size);
1089  ar &boost::serialization::make_array(_data, vec_size);
1090  _end_data = _data + vec_size;
1091  }
1092 }
1093 
1094 
1095 
1096 template < class T >
1097 inline
1098 typename AlignedVector<T>::size_type
1100 {
1101  size_type memory = sizeof(*this);
1102  for (const T *t = _data ; t != _end_data; ++t)
1103  memory += ::MemoryConsumption::memory_consumption(*t);
1104  memory += sizeof(T) * (_end_allocated-_end_data);
1105  return memory;
1106 }
1107 
1108 
1109 #endif // ifndef DOXYGEN
1110 
1111 
1117 template < class T >
1118 bool operator == (const AlignedVector<T> &lhs,
1119  const AlignedVector<T> &rhs)
1120 {
1121  if (lhs.size() != rhs.size())
1122  return false;
1123  for (typename AlignedVector<T>::const_iterator lit = lhs.begin(),
1124  rit = rhs.begin(); lit != lhs.end(); ++lit, ++rit)
1125  if (*lit != *rit)
1126  return false;
1127  return true;
1128 }
1129 
1130 
1131 
1132 
1138 template < class T >
1139 bool operator != (const AlignedVector<T> &lhs,
1140  const AlignedVector<T> &rhs)
1141 {
1142  return !(operator==(lhs, rhs));
1143 }
1144 
1145 
1146 DEAL_II_NAMESPACE_CLOSE
1147 
1148 #endif
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const
#define AssertIndexRange(index, range)
Definition: exceptions.h:1284
void load(Archive &ar, const unsigned int version)
AlignedVector & operator=(const AlignedVector< T > &vec)
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)
#define Assert(cond, exc)
Definition: exceptions.h:1142
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const
void posix_memalign(void **memptr, size_t alignment, size_t size)
Definition: utilities.cc:731
void insert_back(ForwardIterator begin, ForwardIterator end)
size_type memory_consumption() const
void swap(Vector< Number > &u, Vector< Number > &v)
Definition: vector.h:1312
void swap(AlignedVector< T > &vec)
void resize_fast(const size_type size)
void save(Archive &ar, const unsigned int version) const
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()
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const
virtual void apply_to_subrange(const std::size_t begin, const std::size_t end) const
AlignedVectorSet(const std::size_t size, const T &element, T *const destination)
size_type size() const
AlignedVectorDefaultInitialize(const std::size_t size, T *const destination)
void free(T *&pointer)
Definition: cuda.h:84
bool empty() const
size_type capacity() const
std::enable_if< std::is_fundamental< T >::value, std::size_t >::type memory_consumption(const T &t)
reference back()
static ::ExceptionBase & ExcInternalError()