BeeOnRope
BeeOnRope

Reputation: 64905

Aligning buffer to an N-byte boundary but not a 2N-byte one?

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.

Upvotes: 1

Views: 366

Answers (2)

Walter
Walter

Reputation: 45424

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().

Upvotes: 3

Ken Y-N
Ken Y-N

Reputation: 15009

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.

Upvotes: 5

Related Questions