使std::vector分配对齐内存

59

是否可以使自定义结构体的std::vector分配对齐内存,以便使用SIMD指令进行进一步处理?如果可以使用Allocator来实现,是否有人能够分享这样的allocator?


1
你有检查标准分配器是否已经为你做了那件事吗? - TemplateRex
2
@rhalbersma:我认为它不会,因为它不接受对齐参数。 - Violet Giraffe
1
不是我想说什么,而是你的STL实现是否已经为你对齐了内存?你计算了v.begin()的内存地址并检查它是否以X字节的倍数开始吗?即使你无法显式配置对齐方式,std::allocator可能已经帮助你解决了这个问题。 - TemplateRex
2
@VioletGiraffe:更有可能它会在8字节边界上对齐。 - Mooing Duck
16
请注意,使用C++17时,std::vector<__m256>会自动分配32字节对齐的内存 :-) - Marc Glisse
显示剩余4条评论
4个回答

37

编辑:我按照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);
}

需要 C++11,顺便提一下。

我认为你不需要也不应该从std::exception<>std::allocator<>继承。 - GManNickG
1
旧的更好,我不可能在VS 2010中编译这个 :) - Violet Giraffe
我使用了std::allocator作为参考,但删除了大部分的ifdef和其他内容...可能只适用于clang... - znkr
1
未定义的 nail::is_power_of_two - shoosh
@shoosh:实现这个并不难。例如:http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 - Violet Giraffe
显示剩余3条评论

22
在即将发布的1.56版本中,Boost库将包括Boost.Align。 它提供了其他内存对齐助手之一boost::alignment::aligned_allocator,该助手可以用作std::allocator的替代品,并允许您指定对齐方式。 请参阅https://boostorg.github.io/align/上的文档。

3
了解这点很好,但就我个人而言,我觉得将boost(那些不是仅头文件的库)集成到我的项目中相当麻烦。 - Violet Giraffe
9
我同意,集成boost可能有些麻烦。不过,根据我看到的,Boost.Align确实是仅有头文件且仅依赖于其他仅有头文件的库。 - tklauser
2
现在已经可以访问:http://www.boost.org/doc/libs/1_56_0/libs/core/doc/html/index.html - Moncef M.

5
从C++17开始,只需使用std::vector<__m256i>或其他任何对齐的类型。有operator new的对齐版本,它被用于对齐类型的std::allocator(以及普通的new表达式,所以在C++17中,新的__m256i[N]也是安全的)。 @MarcGlisse发表了评论,这里将其作为答案并使其更加明显。

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接