Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making std::vector allocate aligned memory

Is it possible to make std::vector of custom structs allocate aligned memory for further processing with SIMD instructions? If it is possible to do with Allocator, does anyone happen to have such an allocator he could share?

like image 204
Violet Giraffe Avatar asked Oct 17 '12 20:10

Violet Giraffe


People also ask

How do I allocate memory with std::vector?

An std::vector manages its own memory. You can use the reserve() and resize() methods to have it allocate enough memory to fit a given amount of items: std::vector<int> vec1; vec1. reserve(30); // Allocate space for 30 items, but vec1 is still empty.

How much memory does std::vector use?

So there is no surprise regarding std::vector. It uses 4 bytes to store each 4 byte elements. It is very efficient.

What is aligned Alloc?

The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate.

How is a vector stored in memory?

Vectors are assigned memory in blocks of contiguous locations. When the memory allocated for the vector falls short of storing new elements, a new memory block is allocated to vector and all elements are copied from the old location to the new location.


2 Answers

Edit: I removed the inheritance of std::allocator as suggested by GManNickG and made the alignment parameter a compile time thing.

I recently wrote this piece of code. It's not tested as much as I would like it so go on and report errors. :-)

enum class Alignment : size_t {     Normal = sizeof(void*),     SSE    = 16,     AVX    = 32, };   namespace detail {     void* allocate_aligned_memory(size_t align, size_t size);     void deallocate_aligned_memory(void* ptr) noexcept; }   template <typename T, Alignment Align = Alignment::AVX> class AlignedAllocator;   template <Alignment Align> class AlignedAllocator<void, Align> { public:     typedef void*             pointer;     typedef const void*       const_pointer;     typedef void              value_type;      template <class U> struct rebind { typedef AlignedAllocator<U, Align> other; }; };   template <typename T, Alignment Align> class AlignedAllocator { public:     typedef T         value_type;     typedef T*        pointer;     typedef const T*  const_pointer;     typedef T&        reference;     typedef const T&  const_reference;     typedef size_t    size_type;     typedef ptrdiff_t difference_type;      typedef std::true_type propagate_on_container_move_assignment;      template <class U>     struct rebind { typedef AlignedAllocator<U, Align> other; };  public:     AlignedAllocator() noexcept     {}      template <class U>     AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept     {}      size_type     max_size() const noexcept     { return (size_type(~0) - size_type(Align)) / sizeof(T); }      pointer     address(reference x) const noexcept     { return std::addressof(x); }      const_pointer     address(const_reference x) const noexcept     { return std::addressof(x); }      pointer     allocate(size_type n, typename AlignedAllocator<void, Align>::const_pointer = 0)     {         const size_type alignment = static_cast<size_type>( Align );         void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));         if (ptr == nullptr) {             throw std::bad_alloc();         }          return reinterpret_cast<pointer>(ptr);     }      void     deallocate(pointer p, size_type) noexcept     { return detail::deallocate_aligned_memory(p); }      template <class U, class ...Args>     void     construct(U* p, Args&&... args)     { ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }      void     destroy(pointer p)     { p->~T(); } };   template <typename T, Alignment Align> class AlignedAllocator<const T, Align> { public:     typedef T         value_type;     typedef const T*  pointer;     typedef const T*  const_pointer;     typedef const T&  reference;     typedef const T&  const_reference;     typedef size_t    size_type;     typedef ptrdiff_t difference_type;      typedef std::true_type propagate_on_container_move_assignment;      template <class U>     struct rebind { typedef AlignedAllocator<U, Align> other; };  public:     AlignedAllocator() noexcept     {}      template <class U>     AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept     {}      size_type     max_size() const noexcept     { return (size_type(~0) - size_type(Align)) / sizeof(T); }      const_pointer     address(const_reference x) const noexcept     { return std::addressof(x); }      pointer     allocate(size_type n, typename AlignedAllocator<void, Align>::const_pointer = 0)     {         const size_type alignment = static_cast<size_type>( Align );         void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));         if (ptr == nullptr) {             throw std::bad_alloc();         }          return reinterpret_cast<pointer>(ptr);     }      void     deallocate(pointer p, size_type) noexcept     { return detail::deallocate_aligned_memory(p); }      template <class U, class ...Args>     void     construct(U* p, Args&&... args)     { ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }      void     destroy(pointer p)     { p->~T(); } };  template <typename T, Alignment TAlign, typename U, Alignment UAlign> inline bool operator== (const AlignedAllocator<T,TAlign>&, const AlignedAllocator<U, UAlign>&) noexcept { return TAlign == UAlign; }  template <typename T, Alignment TAlign, typename U, Alignment UAlign> inline bool operator!= (const AlignedAllocator<T,TAlign>&, const AlignedAllocator<U, UAlign>&) noexcept { return TAlign != UAlign; } 

The implementation for the actual allocate calls is posix only but you can extent that easily.

void* detail::allocate_aligned_memory(size_t align, size_t size) {     assert(align >= sizeof(void*));     assert(nail::is_power_of_two(align));      if (size == 0) {         return nullptr;     }      void* ptr = nullptr;     int rc = posix_memalign(&ptr, align, size);      if (rc != 0) {         return nullptr;     }      return ptr; }   void detail::deallocate_aligned_memory(void *ptr) noexcept {     return free(ptr); } 

Needs C++11, btw.

like image 90
Florian Avatar answered Oct 04 '22 18:10

Florian


In the upcoming version 1.56, the Boost library will include Boost.Align. Among other memory alignment helpers it provides boost::alignment::aligned_allocator, which can be used a drop-in replacement for std::allocator and allows you to specify an alignment. See the documentation on https://boostorg.github.io/align/

like image 32
tklauser Avatar answered Oct 04 '22 18:10

tklauser