#c #c 11 #c 14 #c 17
#c #управление памятью #выравнивание памяти #распределитель
Вопрос:
Возможно ли сделать std::vector
из пользовательских структур выделение выровненной памяти для дальнейшей обработки с помощью инструкций SIMD? Если это возможно сделать с Allocator
, есть ли у кого-нибудь такой распределитель, которым он мог бы поделиться?
Комментарии:
1. вы проверили, делает ли это уже стандартный распределитель для вас?
2. @rhalbersma: Я не думаю, что это так, он не принимает параметр выравнивания.
3. нет, я имею в виду: ваша реализация STL уже выравнивает память для вас? Вы вычислили адрес памяти
v.begin()
и проверили, начинается ли он с кратного X байтов? даже если вы не можете явно настроить выравнивание, std::allocator уже может помочь вам с этим.4. @VioletGiraffe: более вероятно, что оно выравнивается по 8-байтовой границе.
5. Обратите внимание, что с C 17
std::vector<__m256>
автоматически выделяется память с выравниванием в 32 байта 🙂
Ответ №1:
Редактировать: я удалил наследование 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 Tamp; reference;
typedef const Tamp; 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>amp;) 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, Argsamp;amp;... 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 Tamp; reference;
typedef const Tamp; 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>amp;) 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, Argsamp;amp;... 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>amp;, const AlignedAllocator<U, UAlign>amp;) noexcept
{ return TAlign == UAlign; }
template <typename T, Alignment TAlign, typename U, Alignment UAlign>
inline
bool
operator!= (const AlignedAllocator<T,TAlign>amp;, const AlignedAllocator<U, UAlign>amp;) noexcept
{ return TAlign != UAlign; }
Реализация для фактических вызовов распределения выполняется только в 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(amp;ptr, align, size);
if (rc != 0) {
return nullptr;
}
return ptr;
}
void
detail::deallocate_aligned_memory(void *ptr) noexcept
{
return free(ptr);
}
Нужен C 11, кстати.
Комментарии:
1. Я не думаю, что вам нужно или следует наследовать от <strike>
std::exception<>
</strike>std::allocator<>
.2. @GManNickG, возможно, ты имел в виду
allocator
? 🙂3. @avakar: Ого, даже не заметил, что я это написал!
4. Старый был лучше, я никак не могу скомпилировать это в VS 2010 🙂
5. Не определено
nail::is_power_of_two
Ответ №2:
В предстоящей версии 1.56 библиотека Boost будет включать Boost.Выровнять. Среди других вспомогательных средств выравнивания памяти он предоставляет boost::alignment::aligned_allocator
, которые могут быть использованы в качестве замены для std::allocator
и позволяют указать выравнивание. Смотрите документацию по https://boostorg.github.io/align /
Комментарии:
1. Приятно знать, но лично мне
boost
довольно сложно интегрировать в свои проекты (те библиотеки, которые не предназначены только для заголовков).2.Я согласен, интеграция boost может быть немного болезненной. Однако,
Boost.Align
это только для заголовков, а также зависит только от других библиотек AFAICS, предназначенных только для заголовков.3. Теперь это доступно: boost.org/doc/libs/1_56_0/libs/core/doc/html/index.html
Ответ №3:
Начиная с C 17, просто используйте std::vector<__m256i>
или с любым другим выровненным типом. Существует выровненная версия operator new
, она используется std::allocator
для выровненных типов (а также с помощью простого new
выражения, поэтому new __m256i[N]
также безопасен, начиная с C 17).
В комментарии @MarcGlisse говорится об этом, что делает этот ответ более заметным.
Ответ №4:
Да, это должно быть возможно. Если вы зададите этот вопрос в Google, то получите множество примеров кода, ниже приведены некоторые многообещающие результаты: