1

I would like to allocate some char buffers0, to be passed to an external non-C++ function, that have a specific alignment requirement.

The requirement is that the buffer be aligned to a N-byte1 boundary, but not to a 2N boundary. For example, if N is 64, then an the pointer to this buffer p should satisfy ((uintptr_t)p) % 64 == 0 and ((uintptr_t)p) % 128 != 0 - at least on platforms where pointers have the usual interpretation as a plain address when cast to uintptr_t.

Is there a reasonable way to do this with the standard facilities of C++11?

If not, is there is a reasonable way to do this outside the standard facilities2 which works in practice for modern compilers and platforms?

The buffer will be passed to an outside routine (adhering to the C ABI but written in asm). The required alignment will usually be greater than 16, but less than 8192.

Over-allocation or any other minor wasted-resource issues are totally fine. I'm more interested in correctness and portability than wasting a few bytes or milliseconds.

Something that works on both the heap and stack is ideal, but anything that works on either is still pretty good (with a preference towards heap allocation).


0 This could be with operator new[] or malloc or perhaps some other method that is alignment-aware: whatever makes sense.

1 As usual, N is a power of two.

2 Yes, I understand an answer of this type causes language-lawyers to become apoplectic, so if that's you just ignore this part.

12
  • Where does p come in to this? Are you talking about p as in a pointer to the byte buffer? Commented May 28, 2017 at 22:58
  • Yes, as a practical matter p is a pointer the byte buffer (which is why I called it p). I was slightly cagey in the actual question by calling p an "address" rather than a pointer, because if I called it a pointer a bunch of lawyers would jump down my throat pointing out that arithmetic cannot be done directly on pointers, and going on about the platform-specifics of intptr_t, etc. Commented May 28, 2017 at 22:59
  • 1
    @t0mm13b - I didn't follow the first part of your last comment regarding "the compiler will infer ... management of memory". I added the x86 and arm tags since those are the platforms I'm most interested in, so if there is a solution of the "not standard, but works in practice variety" I want it to at least work there. I don't think the question is vague because I'm at least asking if there is a standard way to do this, and the "as a practical matter, this works..." part is usually just implied. I shouldn't be punished for mentioning it explicitly. Commented May 28, 2017 at 23:08
  • 3
    the requirement that (p % 64 == 0) && (p % 128 != 0) is most peculiar. May I ask where this does come from? In particular the second condition is very odd. Commented May 28, 2017 at 23:11
  • 1
    Either you allocate every time twice the memory you need and then you go hunting for the only address that satisfies your requirement, or you just ask for a big slab of memory at program start and write your own bizarro allocator. Commented May 28, 2017 at 23:15

2 Answers 2

5

Logically, to satisfy "aligned to N, but not 2N", we align to 2N then add N to the pointer. Note that this will over-allocate N bytes.

So, assuming we want to allocate B bytes, if you just want stack space, alignas would work, perhaps.

alignas(N*2) char buffer[B+N];
char *p = buffer + N;

If you want heap space, std::aligned_storage might do:

typedef std::aligned_storage<B+N,N*2>::type ALIGNED_CHAR;
ALIGNED_CHAR buffer;
char *p = reinterpret_cast<char *>(&buffer) + N;

I've not tested either out, but the documentation suggests it should be OK.

Sign up to request clarification or add additional context in comments.

3 Comments

I've heard this works only up to 16-byte alignment, so since you are using N*2 it would work only up to 8-byte alignment which is pretty useless.
I don't know what the standard says, but Visual C++ 2017 states it supports up to 8198 bytes. GNU g++ seems to support 128 bytes.
@KenY-N: 8192 (1<<13) actually
3

You can use _aligned_malloc(nbytes,alignment) (in MSVC) or _mm_malloc(nbytes,alignment) (on other compilers) to allocate (on the heap) nbytes of memory aligned to alignment bytes, which must be an integer power of two.

Then you can use the trick from Ken's answer to avoid alignment to 2N:

void*ptr_alloc = _mm_malloc(nbytes+N,2*N);
void*ptr = static_cast<void*>(static_cast<char*>(ptr_alloc) + N);

/* do your number crunching */

_mm_free(ptr_alloc);

We must ensure to keep the pointer returned by _mm_malloc() for later de-allocation, which must be done via _mm_free().

3 Comments

Thanks! Is the static_cast to and from void * legal? Will it return a resaonable value (i.e., at least something within the region of memory pointed to by ptr_alloc)?
The static_cast should be legal, why not? It does not change the value of the pointer, but merely changes it's type from void* to char* so that the pointer arithmetic with + works correctly.
Right, but it was mostly about the cast back and what you can subsequently do with the pointer. There are a lot of caveats around pointer casting and subsequent use in c++ so my usual assumption is you can't do that! - but I didn't find any particular reference that would say this isn't OK. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.