简体   繁体   English

使 std::vector 分配对齐的内存

[英]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?是否可以使自定义结构的std::vector分配对齐的内存以使用 SIMD 指令进行进一步处理? If it is possible to do with Allocator , does anyone happen to have such an allocator he could share?如果可以使用Allocator ,有没有人碰巧有这样一个他可以共享的分配器?

Edit: I removed the inheritance of std::allocator as suggested by GManNickG and made the alignment parameter a compile time thing.编辑:我按照 GManNickG 的建议删除了std::allocator的继承,并使对齐参数成为编译时的事情。

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.实际分配调用的实现仅是 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);
}

Needs C++11, btw.需要 C++11,顺便说一句。

In the upcoming version 1.56, the Boost library will include Boost.Align.在即将发布的 1.56 版本中,Boost 库将包含 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.在其他内存对齐助手中,它提供了boost::alignment::aligned_allocator ,它可以用作std::allocator替代品,并允许您指定对齐方式。 See the documentation on https://boostorg.github.io/align/请参阅https://boostorg.github.io/align/上的文档

Yes, it should be possible.是的,应该可以。 If you put this question on google then you will get lots of sample code, below is some promising results:如果你把这个问题放在 google 上,你会得到很多示例代码,下面是一些有希望的结果:

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

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

https://gist.github.com/1471329 https://gist.github.com/1471329

Starting in C++17, just use std::vector<__m256i> or with any other aligned type.从 C++17 开始,只需使用std::vector<__m256i>或任何其他对齐类型。 There's aligned version of operator new , it is used by std::allocator for aligned types (as well as by plain new -expression, so new __m256i[N] is also safe starting in C++17).还有的对准的版本operator new ,它是用来通过std::allocator用于对准类型(以及由纯new -expression,所以新__m256i[N]也是安全的起始在C ++ 17)。

There's a comment by @MarcGlisse saying this, making this an answer to make it more visible. @MarcGlisse 对此发表了评论,这是一个使其更明显的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM