Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aligned dynamic array and smart pointer

I frequently need to align the start of a dynamic array to a 16, 32, or 64 Byte boundary for vectorization, e.g., for SSE, AVX, AVX-512. I am looking for a transparent and safe way to use this in conjunction with smart pointers, in particular std::unique_ptr.

Given an implementation of allocation and deallocation routines, say

template<class T>
T * allocate_aligned(int alignment, int length)
{
    // omitted: check minimum alignment, check error
    T * raw = 0;
    // using posix_memalign as an example, could be made platform dependent...
    int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length);
    return raw;
}

template<class T>
struct DeleteAligned
{
    void operator()(T * data) const
    {
        free(data);
    }
};

I would like to do something like this

std::unique_ptr<float[]> data(allocate_aligned<float>(alignment, length));

but I could not figure out how to do get unique_ptr to use the proper Deleter without requiring from the user to specify it (which is a potential cause for errors). The alternative I found was to use a template alias

template<class T>
using aligned_unique_ptr = std::unique_ptr<T[], DeleteAligned<T>>;

Then we can use

aligned_unique_ptr<float> data(allocate_aligned<float>(alignment, length));

The remaining problem is that nothing keeps the user from putting the raw pointer into a std::unique_ptr.

Apart from that, do you see anything wrong with this? Is there an alternative which is less error prone, but completely transparent to the user after the allocation was done?

like image 233
Simon Avatar asked Apr 02 '15 09:04

Simon


1 Answers

You should never return an owning raw pointer. allocate_aligned violates that. Change it to return the appropriate smart pointer instead:

template<class T>
std::unique_ptr<T[], DeleteAligned<T>> allocate_aligned(int alignment, int length)
{
    // omitted: check minimum alignment, check error
    T * raw = 0;
    // using posix_memalign as an example, could be made platform dependent...
    int error = posix_memalign((void **)&raw, alignment, sizeof(T)*length);
    return std::unique_ptr<T[], DeleteAligned<T>>{raw};
}

This way, no client can put the raw pointer into an inappropriate smart pointer because they never get the raw pointer in the first place. And you prevent memory leaks from accidentally not putting the raw pointer in a smart one at all.

As @KonradRudolph pointed out, the standard itself is going this way—in C++14, std::make_unique is exactly such a wrapper for plain new.

like image 138
Angew is no longer proud of SO Avatar answered Sep 28 '22 09:09

Angew is no longer proud of SO