deal.II version GIT relicensing-2289-g1e5549a87a 2024-12-21 21:30:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
Classes | Public Types | Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | Related Symbols | List of all members
AlignedVector< T > Class Template Reference

#include <deal.II/base/aligned_vector.h>

Classes

class  Deleter
 

Public Types

using value_type = T
 
using pointer = value_type *
 
using const_pointer = const value_type *
 
using iterator = value_type *
 
using const_iterator = const value_type *
 
using reference = value_type &
 
using const_reference = const value_type &
 
using size_type = std::size_t
 

Public Member Functions

 AlignedVector ()
 
template<typename RandomAccessIterator , typename = std::enable_if_t<std::is_convertible_v< typename std::iterator_traits<RandomAccessIterator>::iterator_category, std::random_access_iterator_tag>>>
 AlignedVector (RandomAccessIterator begin, RandomAccessIterator end)
 
 AlignedVector (const size_type size, const T &init=T())
 
 ~AlignedVector ()=default
 
 AlignedVector (const AlignedVector< T > &vec)
 
 AlignedVector (AlignedVector< T > &&vec) noexcept
 
AlignedVectoroperator= (const AlignedVector< T > &vec)
 
AlignedVectoroperator= (AlignedVector< T > &&vec) noexcept
 
void resize_fast (const size_type new_size)
 
void resize (const size_type new_size)
 
void resize (const size_type new_size, const T &init)
 
void reserve (const size_type new_allocated_size)
 
void shrink_to_fit ()
 
void clear ()
 
void push_back (const T in_data)
 
reference back ()
 
const_reference back () const
 
template<typename ForwardIterator >
void insert_back (ForwardIterator begin, ForwardIterator end)
 
template<typename RandomAccessIterator , typename = std::enable_if_t<std::is_convertible_v< typename std::iterator_traits<RandomAccessIterator>::iterator_category, std::random_access_iterator_tag>>>
iterator insert (const_iterator position, RandomAccessIterator begin, RandomAccessIterator end)
 
void fill ()
 
void fill (const T &element)
 
void replicate_across_communicator (const MPI_Comm communicator, const unsigned int root_process)
 
void swap (AlignedVector< T > &vec) noexcept
 
bool empty () const
 
size_type size () const
 
size_type capacity () const
 
reference operator[] (const size_type index)
 
const_reference operator[] (const size_type index) const
 
pointer data ()
 
const_pointer data () const
 
iterator begin ()
 
iterator end ()
 
const_iterator begin () const
 
const_iterator end () const
 
size_type memory_consumption () const
 
template<class Archive >
void save (Archive &ar, const unsigned int version) const
 
template<class Archive >
void load (Archive &ar, const unsigned int version)
 
template<class Archive >
void serialize (Archive &archive, const unsigned int version)
 

Static Public Member Functions

static ::ExceptionBaseExcAlignedVectorChangeAfterReplication ()
 

Private Member Functions

void allocate_and_move (const size_t old_size, const size_t new_size, const size_t new_allocated_size)
 

Private Attributes

std::unique_ptr< T[], Deleterelements
 
T * used_elements_end
 
T * allocated_elements_end
 
bool replicated_across_communicator
 

Related Symbols

(Note that these are not member symbols.)

template<class T >
bool operator== (const AlignedVector< T > &lhs, const AlignedVector< T > &rhs)
 
template<class T >
bool operator!= (const AlignedVector< T > &lhs, const AlignedVector< T > &rhs)
 

Detailed Description

template<class T>
class AlignedVector< T >

This is a replacement class for std::vector to be used in combination with VectorizedArray and derived data types. It allocates memory aligned to addresses of a vectorized data type (in order to avoid segmentation faults when a variable of type VectorizedArray which the compiler assumes to be aligned to certain memory addresses does not actually follow these rules). This could also be achieved by proving std::vector with a user-defined allocator. On the other hand, writing an own small vector class lets us implement parallel copy and move operations with TBB, insert deal.II-style assertions, and cut some unnecessary functionality. Note that this vector is a bit more memory-consuming than std::vector because of alignment, so it is recommended to only use this vector on long vectors.

Definition at line 60 of file aligned_vector.h.

Member Typedef Documentation

◆ value_type

template<class T >
using AlignedVector< T >::value_type = T

Declare standard types used in all containers. These types parallel those in the C++ standard libraries vector<...> class.

Definition at line 67 of file aligned_vector.h.

◆ pointer

template<class T >
using AlignedVector< T >::pointer = value_type *

Definition at line 68 of file aligned_vector.h.

◆ const_pointer

template<class T >
using AlignedVector< T >::const_pointer = const value_type *

Definition at line 69 of file aligned_vector.h.

◆ iterator

template<class T >
using AlignedVector< T >::iterator = value_type *

Definition at line 70 of file aligned_vector.h.

◆ const_iterator

template<class T >
using AlignedVector< T >::const_iterator = const value_type *

Definition at line 71 of file aligned_vector.h.

◆ reference

template<class T >
using AlignedVector< T >::reference = value_type &

Definition at line 72 of file aligned_vector.h.

◆ const_reference

template<class T >
using AlignedVector< T >::const_reference = const value_type &

Definition at line 73 of file aligned_vector.h.

◆ size_type

template<class T >
using AlignedVector< T >::size_type = std::size_t

Definition at line 74 of file aligned_vector.h.

Constructor & Destructor Documentation

◆ AlignedVector() [1/5]

template<class T >
AlignedVector< T >::AlignedVector ( )

Empty constructor. Sets the vector size to zero.

◆ AlignedVector() [2/5]

template<class T >
template<typename RandomAccessIterator , typename = std::enable_if_t<std::is_convertible_v< typename std::iterator_traits<RandomAccessIterator>::iterator_category, std::random_access_iterator_tag>>>
AlignedVector< T >::AlignedVector ( RandomAccessIterator  begin,
RandomAccessIterator  end 
)

Range constructor.

Note
Unlike std::vector, this constructor uses random-access iterators so that the copy may be parallelized.
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ AlignedVector() [3/5]

template<class T >
AlignedVector< T >::AlignedVector ( const size_type  size,
const T &  init = T() 
)
explicit

Set the vector size to the given size and initializes all elements with T().

Note
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ ~AlignedVector()

template<class T >
AlignedVector< T >::~AlignedVector ( )
default

Destructor.

◆ AlignedVector() [4/5]

template<class T >
AlignedVector< T >::AlignedVector ( const AlignedVector< T > &  vec)

Copy constructor.

Note
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ AlignedVector() [5/5]

template<class T >
AlignedVector< T >::AlignedVector ( AlignedVector< T > &&  vec)
noexcept

Move constructor. Create a new aligned vector by stealing the contents of vec.

Member Function Documentation

◆ operator=() [1/2]

template<class T >
AlignedVector & AlignedVector< T >::operator= ( const AlignedVector< T > &  vec)

Assignment to the input vector vec.

Note
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ operator=() [2/2]

template<class T >
AlignedVector & AlignedVector< T >::operator= ( AlignedVector< T > &&  vec)
noexcept

Move assignment operator.

◆ resize_fast()

template<class T >
void AlignedVector< T >::resize_fast ( const size_type  new_size)

Change the size of the vector. If the new size is larger than the previous size, then new elements will be added to the end of the vector; these elements will remain uninitialized (i.e., left in an undefined state) if std::is_trivially_default_constructible_v<T> is true, and will be default initialized if that type trait is false. See here for a precise definition of std::is_trivially_default_constructible.

If the new size is less than the previous size, then the new_sizeth and all subsequent elements will be destroyed.

As a consequence of the outline above, the "_fast" suffix of this function refers to the fact that for trivially default constructible types T, this function omits the initialization of new elements.

Note
This method can only be invoked for classes T that define a default constructor, T(). Otherwise, compilation will fail.

◆ resize() [1/2]

template<class T >
void AlignedVector< T >::resize ( const size_type  new_size)

Change the size of the vector. It keeps old elements previously available, and initializes each newly added element to a default-constructed object of type 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. The destructors of released elements are also called.

Note
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ resize() [2/2]

template<class T >
void AlignedVector< T >::resize ( const size_type  new_size,
const T &  init 
)

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.
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ reserve()

template<class T >
void AlignedVector< T >::reserve ( const size_type  new_allocated_size)

Reserve memory space for new_allocated_size elements.

If the argument new_allocated_size is less than the current number of stored elements (as indicated by calling size()), then this function does not do anything at all. Except if the argument new_allocated_size is set to zero, then all previously allocated memory is released (this operation then being equivalent to directly calling the clear() function).

In order to avoid too frequent reallocation (which involves copy of the data), this function doubles the amount of memory occupied when the given size is larger than the previously allocated size.

Note that this function only changes the amount of elements the object can store, but not the number of elements it actually stores. As a consequence, no constructors or destructors of newly created objects are run, though the existing elements may be moved to a new location (which involves running the move constructor at the new location and the destructor at the old location).

◆ shrink_to_fit()

template<class T >
void AlignedVector< T >::shrink_to_fit ( )

Releases the memory allocated but not used.

◆ clear()

template<class T >
void AlignedVector< T >::clear ( )

Releases all previously allocated memory and leaves the vector in a state equivalent to the state after the default constructor has been called.

◆ push_back()

template<class T >
void AlignedVector< T >::push_back ( const T  in_data)

Inserts an element at the end of the vector, increasing the vector size by one. Note that the allocated size will double whenever the previous space is not enough to hold the new element.

◆ back() [1/2]

template<class T >
reference AlignedVector< T >::back ( )

Return the last element of the vector (read and write access).

◆ back() [2/2]

template<class T >
const_reference AlignedVector< T >::back ( ) const

Return the last element of the vector (read-only access).

◆ insert_back()

template<class T >
template<typename ForwardIterator >
void AlignedVector< T >::insert_back ( ForwardIterator  begin,
ForwardIterator  end 
)

Inserts several elements at the end of the vector given by a range of elements.

◆ insert()

template<class T >
template<typename RandomAccessIterator , typename = std::enable_if_t<std::is_convertible_v< typename std::iterator_traits<RandomAccessIterator>::iterator_category, std::random_access_iterator_tag>>>
iterator AlignedVector< T >::insert ( const_iterator  position,
RandomAccessIterator  begin,
RandomAccessIterator  end 
)

Insert the range specified by begin and end after the element position.

Note
Unlike std::vector, this function uses random-access iterators so that the copy may be parallelized.
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ fill() [1/2]

template<class T >
void AlignedVector< T >::fill ( )

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.
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ fill() [2/2]

template<class T >
void AlignedVector< T >::fill ( const T &  element)

Fills the vector with size() copies of the given input.

Note
This method can only be invoked for classes that define the copy assignment operator. Otherwise, compilation will fail.
If deal.II is configured with threads, this operation will run multi-threaded by splitting the work into smaller chunks (assuming there is enough work to make this worthwhile).

◆ replicate_across_communicator()

template<class T >
void AlignedVector< T >::replicate_across_communicator ( const MPI_Comm  communicator,
const unsigned int  root_process 
)

This function replicates the state found on the process indicated by root_process across all processes of the MPI communicator. The current state found on any of the processes other than root_process is lost in this process. One can imagine this operation to act like a call to Utilities::MPI::broadcast() from the root process to all other processes, though in practice the function may try to move the data into shared memory regions on each of the machines that host MPI processes and let all MPI processes on this machine then access this shared memory region instead of keeping their own copy.

The intent of this function is to quickly exchange large arrays from one process to others, rather than having to compute or create it on all processes. This is specifically the case for data loaded from disk – say, large data tables – that are more easily dealt with by reading once and then distributing across all processes in an MPI universe, than letting each process read the data from disk itself. Specifically, the use of shared memory regions allows for replicating the data only once per multicore machine in the MPI universe, rather than replicating data once for each MPI process. This results in large memory savings if the data is large on today's machines that can easily house several dozen MPI processes per shared memory space. This use case is outlined in the TableBase class documentation as the current function is called from TableBase::replicate_across_communicator(). Indeed, the primary rationale for this function is to enable sharing data tables based on TableBase across MPI processes.

This function does not imply a model of keeping data on different processes in sync, as LinearAlgebra::distributed::Vector and other vector classes do where there exists a notion of certain elements of the vector owned by each process and possibly ghost elements that are mirrored from its owning process to other processes. Rather, the elements of the current object are simply copied to the other processes, and it is useful to think of this operation as creating a set of const AlignedVector objects on all processes that should not be changed any more after the replication operation, as this is the only way to ensure that the vectors remain the same on all processes. This is particularly true because of the use of shared memory regions where any modification of a vector element on one MPI process may also result in a modification of elements visible on other processes, assuming they are located within one shared memory node.

Note
The use of shared memory between MPI processes requires that the detected MPI installation supports the necessary operations. This is the case for MPI 3.0 and higher.
This function is not cheap. It needs to create sub-communicators of the provided communicator object, which is generally an expensive operation. Likewise, the generation of shared memory spaces is not a cheap operation. As a consequence, this function primarily makes sense when the goal is to share large read-only data tables among processes; examples are data tables that are loaded at start-up time and then used over the course of the run time of the program. In such cases, the start-up cost of running this function can be amortized over time, and the potential memory savings from not having to store the table on each process may be substantial on machines with large core counts on which many MPI processes run on the same machine.
This function only makes sense if the data type T is "self-contained", i.e., all if its information is stored in its member variables, and if none of the member variables are pointers to other parts of the memory. This is because if a type T does have pointers to other parts of memory, then moving T into a shared memory space does not result in the other processes having access to data that the object points to with its member variable pointers: These continue to live only on one process, and are typically in memory areas not accessible to the other processes. As a consequence, the usual use case for this function is to share arrays of simple objects such as doubles or ints.
After calling this function, objects on different MPI processes share a common state. That means that certain operations become "collective", i.e., they must be called on all participating processors at the same time. In particular, you can no longer call resize(), reserve(), or clear() on one MPI process – you have to do so on all processes at the same time, because they have to communicate for these operations. If you do not do so, you will likely get a deadlock that may be difficult to debug. By extension, this rule of only collectively resizing extends to this function itself: You can not call it twice in a row because that implies that first all but the root_process throw away their data, which is not a collective operation. Generally, these restrictions on what can and can not be done hint at the correctness of the comments above: You should treat an AlignedVector on which the current function has been called as const, on which no further operations can be performed until the destructor is called.

◆ swap()

template<class T >
void AlignedVector< T >::swap ( AlignedVector< T > &  vec)
noexcept

Swaps the given vector with the calling vector.

◆ empty()

template<class T >
bool AlignedVector< T >::empty ( ) const

Return whether the vector is empty, i.e., its size is zero.

◆ size()

template<class T >
size_type AlignedVector< T >::size ( ) const

Return the size of the vector.

◆ capacity()

template<class T >
size_type AlignedVector< T >::capacity ( ) const

Return the capacity of the vector, i.e., the size this vector can hold without reallocation. Note that capacity() >= size().

◆ operator[]() [1/2]

template<class T >
reference AlignedVector< T >::operator[] ( const size_type  index)

Read-write access to entry index in the vector.

◆ operator[]() [2/2]

template<class T >
const_reference AlignedVector< T >::operator[] ( const size_type  index) const

Read-only access to entry index in the vector.

◆ data() [1/2]

template<class T >
pointer AlignedVector< T >::data ( )

Return a pointer to the underlying data buffer.

◆ data() [2/2]

template<class T >
const_pointer AlignedVector< T >::data ( ) const

Return a const pointer to the underlying data buffer.

◆ begin() [1/2]

template<class T >
iterator AlignedVector< T >::begin ( )

Return a read and write pointer to the beginning of the data array.

◆ end() [1/2]

template<class T >
iterator AlignedVector< T >::end ( )

Return a read and write pointer to the end of the data array.

◆ begin() [2/2]

template<class T >
const_iterator AlignedVector< T >::begin ( ) const

Return a read-only pointer to the beginning of the data array.

◆ end() [2/2]

template<class T >
const_iterator AlignedVector< T >::end ( ) const

Return a read-only pointer to the end of the data array.

◆ memory_consumption()

template<class T >
size_type AlignedVector< T >::memory_consumption ( ) const

Return the memory consumption of the allocated memory in this class. If the underlying type T allocates memory by itself, this memory is not counted.

◆ save()

template<class T >
template<class Archive >
void AlignedVector< T >::save ( Archive &  ar,
const unsigned int  version 
) const

Write the data of this object to a stream for the purpose of serialization using the BOOST serialization library.

◆ load()

template<class T >
template<class Archive >
void AlignedVector< T >::load ( Archive &  ar,
const unsigned int  version 
)

Read the data of this object from a stream for the purpose of serialization using the BOOST serialization library.

◆ serialize()

template<class T >
template<class Archive >
void AlignedVector< T >::serialize ( Archive &  archive,
const unsigned int  version 
)

Write and read the data of this object from a stream for the purpose of serialization using the BOOST serialization library.

◆ allocate_and_move()

template<class T >
void AlignedVector< T >::allocate_and_move ( const size_t  old_size,
const size_t  new_size,
const size_t  new_allocated_size 
)
private

Make a new allocation, move the data from the old memory region to the new region, and release the old memory.

Friends And Related Symbol Documentation

◆ operator==()

template<class T >
bool operator== ( const AlignedVector< T > &  lhs,
const AlignedVector< T > &  rhs 
)
related

Relational operator == for AlignedVector

Definition at line 2212 of file aligned_vector.h.

◆ operator!=()

template<class T >
bool operator!= ( const AlignedVector< T > &  lhs,
const AlignedVector< T > &  rhs 
)
related

Relational operator != for AlignedVector

Definition at line 2234 of file aligned_vector.h.

Member Data Documentation

◆ elements

template<class T >
std::unique_ptr<T[], Deleter> AlignedVector< T >::elements
private

Pointer to actual data array, using the custom deleter class above.

Definition at line 730 of file aligned_vector.h.

◆ used_elements_end

template<class T >
T* AlignedVector< T >::used_elements_end
private

Pointer to one past the last valid value.

Definition at line 735 of file aligned_vector.h.

◆ allocated_elements_end

template<class T >
T* AlignedVector< T >::allocated_elements_end
private

Pointer to the end of the allocated memory.

Definition at line 740 of file aligned_vector.h.

◆ replicated_across_communicator

template<class T >
bool AlignedVector< T >::replicated_across_communicator
private

Flag indicating if replicate_across_communicator() has been called.

Definition at line 745 of file aligned_vector.h.


The documentation for this class was generated from the following file: