Заставить std :: vector выделять выровненную память

Можно ли сделать std::vector пользовательских структур выделяют выровненную память для дальнейшей обработки с помощью SIMD-инструкций? Если это возможно сделать с AllocatorУ кого-нибудь есть такой распределитель, которым он может поделиться?

43

Решение

Редактировать: Я удалил наследство std::allocator как предложено GManNickG и сделало параметр выравнивания вещью времени компиляции.

Я недавно написал этот кусок кода. Это не проверено столько, сколько я хотел бы, так что продолжайте и сообщать об ошибках. 🙂

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; }

Реализация для фактических вызовов allocate только posix, но вы можете легко это расширить.

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);
}

Требуется C ++ 11, кстати.

26

Другие решения

В следующей версии 1.56 библиотека Boost будет включать Boost.Align. Среди других помощников выравнивания памяти это обеспечивает boost::alignment::aligned_allocator, который может быть использован в качестве замены для std::allocator и позволяет указать выравнивание. Смотрите документацию на https://boostorg.github.io/align/

20

Да, это должно быть возможно. Если вы зададите этот вопрос в Google, вы получите много примеров кода, ниже приведены некоторые многообещающие результаты:

https://bitbucket.org/marten/alignedallocator/wiki/Home

http://code.google.com/p/mastermind-strategy/source/browse/trunk/src/util/aligned_allocator.hpp?r=167

https://gist.github.com/1471329

3