是否可以使自定义结构体的std::vector
分配对齐内存,以便使用SIMD指令进行进一步处理?如果可以使用Allocator
来实现,是否有人能够分享这样的allocator?
是否可以使自定义结构体的std::vector
分配对齐内存,以便使用SIMD指令进行进一步处理?如果可以使用Allocator
来实现,是否有人能够分享这样的allocator?
编辑:我按照GManNickG的建议删除了std::allocator
的继承,并将对齐参数设置为编译时参数。
最近我写了这段代码。由于测试不够充分,如果有错误请指出。 :-)
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; }
实际分配调用的实现仅限于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);
}
std::exception<>
或std::allocator<>
继承。 - GManNickGnail::is_power_of_two
。 - shooshboost::alignment::aligned_allocator
,该助手可以用作std::allocator
的替代品,并允许您指定对齐方式。 请参阅https://boostorg.github.io/align/上的文档。boost
(那些不是仅头文件的库)集成到我的项目中相当麻烦。 - Violet GiraffeBoost.Align
确实是仅有头文件且仅依赖于其他仅有头文件的库。 - tklauserstd::vector<__m256i>
或其他任何对齐的类型。有operator new
的对齐版本,它被用于对齐类型的std::allocator
(以及普通的new
表达式,所以在C++17中,新的__m256i[N]
也是安全的)。 @MarcGlisse发表了评论,这里将其作为答案并使其更加明显。是的,这是可能的。如果您在谷歌上提出这个问题,您将得到很多样例代码,下面是一些有前途的结果:
v.begin()
的内存地址并检查它是否以X字节的倍数开始吗?即使你无法显式配置对齐方式,std::allocator可能已经帮助你解决了这个问题。 - TemplateRexstd::vector<__m256>
会自动分配32字节对齐的内存 :-) - Marc Glisse