Skip to content
Advertisement

Is there a linux equivalent of _aligned_realloc

Is there a linux equivalent of _aligned_realloc?

I want to use realloc so I don’t have to memcpy the data every time I resize it. Am I stuck with mmap? I only used mmap once is there a recommended way of implementing memory that will be resized a few times? I’m assuming I can’t mix mmap with aligned_alloc and I would have to do memcpy for the first resize? (or always use mmap)

The realloc below doesn’t always align. I tested under (64bit) linux using gcc and clang

#include<cstdlib>
#include<cstdio>
#define ALIGNSIZE 64
int main()
{
    for(int i = 0; i<10; i++)
    {
        void *p = aligned_alloc(ALIGNSIZE, 4096);
        void *p2 = realloc(p, 4096+2048); //This doesn't always align
        //void *p3 = aligned_alloc(ALIGNSIZE, 4096/24); //Doesn't need this line to make it unaligned. 

        if (((long)p & (ALIGNSIZE-1)) != 0 || ((long)p2 & (ALIGNSIZE-1)) != 0)
            printf("%d %d %dn", i, ((long)p & (ALIGNSIZE-1)) != 0, ((long)p2 & (ALIGNSIZE-1)) != 0);
    }
}

Advertisement

Answer

No, there is neither standard alternative in C++, nor in POSIX standard, nor in the GNU C library.

Here is a proof of concept using only standard functions:

void*
aligned_realloc_optimistic(
    void* ptr, std::size_t new_size, std::size_t alignment)
{
    void* reallocated = std::realloc(ptr, new_size);
    return is_aligned(reallocated, alignment) // see below
        ? reallocated
        : aligned_realloc_pessimistic(reallocated, new_size, new_size, alignment);
        // see below
}

As pointed out in the comments: This has the caveat that in the worst case std::realloc may fail to reuse the allcation and also happens to return an misaligned pointer, then we allocate twice.

We can skip attempting to realloc by just unconditionally allocting, copying and freeing which removes both the worst case of double allocation and the best case of no allocation:

void*
aligned_realloc_pessimistic(
    void* ptr, std::size_t new_size, std::size_t old_size, std::size_t alignment)
{
    void* aligned = std::aligned_alloc(alignment, new_size);
    std::memcpy(aligned, ptr, old_size);
    std::free(ptr);
    return aligned;
}

The obvious problem with this is that we have to know the old size which is was not required with the regular reallocation.

By relying on system specific functions, we can keep the optimal case of avoiding allocation and avoid double allocation and also not require knowing the old size:

void*
aligned_realloc_glibc(
    void* ptr, std::size_t new_size, std::size_t alignment)
{
    auto old_size = malloc_usable_size(ptr); // GNU extension
    return old_size >= new_size && is_aligned(ptr, alignment)
        ? ptr
        : aligned_realloc_pessimistic(ptr, new_size, old_size, alignment);
}

The helper function used above:

bool is_aligned(void* ptr, std::size_t alignment)
{
    std::size_t space = 1;
    return std::align(alignment, space, ptr, space);
}
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement