Is there a linux equivalent of _aligned_realloc
Asked Answered
C

2

5

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 %d\n", i, ((long)p & (ALIGNSIZE-1)) != 0, ((long)p2 & (ALIGNSIZE-1)) != 0);
    }
}
Camp answered 17/11, 2020 at 23:12 Comment(8)
No, realloc() does not guarantee an arbitrary alignment, only the machine word.Ishtar
Can you explain in your question why you need a specific alignment? malloc, realloc etc. are guaranteed to return an address that is suitable as a pointer to any data type on the current platform. This means on a 64-bit system it is at least aligned to an address suitable for a 64-bit value, i.e. a multiple of 8 bytes.Tankard
@Tankard but not for 32/64/128 byte aligned simd-suitable data.Papaw
@Ishtar Not "machine word" but maximum natural alignment. Which is often greater than a word.Parkin
@Tankard SIMD and occasionally I want data that starts at the beginning of a cache line (so I know another thread won't touch it by accessing data next to it)Camp
@EricStotch You should edit your question to add information instead of writing comments.Tankard
@Tankard Uhhh. I was answering your question. I could want 64byte aligned data for any reason and it wouldn't change the current questionCamp
@EricStotch In your case it might not be important, but often someone may come up with a completely different solution for your use case if enough background information is available. That's why it is useful to provide this information in the question. This will also help other people with similar questions to understand your question and to decide if it matches their use case. That's why I recommend to add the information from your comment to the question.Tankard
P
8

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);
}
Parkin answered 17/11, 2020 at 23:37 Comment(9)
Sometimes does multiple reallocations... Perhaps it's better to aligned_alloc, memcpy, and free right from the start?Kharkov
@Kharkov I suppose it depends on likelyhood of realloc being able to avoid allocation. This is the optimistic approach. If only there was a way to test whether realloc would allocate beforehand, we could both make use of it and avoid the case of allocating twice.Parkin
Correct me if I'm wrong, but isn't this "proof-of-concept" dangerous? A pointer that gets readjusted by std::align would not be able to be std::freed because it's not the same pointer returned by any allocation function (std::aligned_alloc, std::realloc, etc)Conglutinate
@Human-Compiler The pointer won't be adjusted by std::align since it has not been given space to adjust.Parkin
Ahh, I see. Is that guaranteed by the standard that a 0 value for size won't just return nullptr though?Conglutinate
@Human-Compiler Good question. We might as well use any other number to avoid that in case it is an issue. Changed to 1Parkin
I like this much more than my mmap thought. I hate that this is the way to do it. I guess another set of functions to add to my personal standard libsCamp
@Kharkov Now that I wrote it, I realize that the problem with just aligned_alloc, memcpy, and free is that we cannot know how much to copy.Parkin
In general, you should expect, in any nontrivial-allocation-using program, that realloc has to allocate new memory and memcpy at least once per geometric growth of the size, so this solution really isn't bad. Just make sure you aren't increasing the size linearly, which can give really bad worst-case performance. Always increase by some factor, not necessarily doubling but at least += half or 1/4 of the current size.Boulogne
E
4

Am I stuck with mmap?

If you want maximum flexibility, there is no better way than using the platform's low-level allocation mechanism.

In this case, you want mremap(), possibly with MREMAP_MAYMOVE. It does exactly what you need (if you need alignments bigger than a page you will need to take care of that manually).

Edroi answered 18/11, 2020 at 0:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.