Reputation: 6255
I want to align a pointer p so that p = a (modulo b)
. Most of the time, a = 0 and b = 32 or 64 for SIMD alignement, but I might also want a = 64 and b = 128 when I want to fine tune my algorithm for cache associativity.
std::align
does not accept any power of 2 for alignement. It will be fixed in C++17, but it is useless right now__mm_malloc/__free
is not as portable as I wantMy best solution so far is to cast the pointer to a std::uintptr_t
and use modulo operations on integers to move the pointer. Unfortunately, it is not portable as casting a pointer to std::uintptr_t
is not "allowed". But it works on all platforms I have tried so far.
On what kind of platform would such a code break?
Upvotes: 4
Views: 1715
Reputation: 3194
The following C++ code requires that alignment
is a power of 2. Memory allocated wit malloc_aligned
must be released with free_aligned
:
void *
malloc_aligned(std::size_t alignment, std::size_t size)
{
alignment = std::max(alignment, alignof(void *));
size += alignment;
void *ptr = std::malloc(size);
void *ptr2 = (void *)(((uintptr_t)ptr + alignment) & ~(alignment-1));
void **vp = (void**) ptr2 - 1;
*vp = ptr;
return ptr2;
}
void
free_aligned(void *ptr)
{
std::free(*((void**)ptr-1));
}
Upvotes: 1
Reputation: 238481
std::align does not accept any power of 2 for alignement. I will be fixed in C++17, but it is useless right now
Let's see what the standard (draft) says.
[ptr.align]
2 Requires:
(2.1) — alignment shall be a fundamental alignment value or an extended alignment value supported by the implementation in this context.
So, any fundamental alignment value is allowed, and possibly others if allowed by the implementation. Let's see what a fundamental alignment is.
[basic.align]
2 A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to alignof(std::max_align_t)...
Ok. Then there is a limitation to all alignments.
4 Alignments are represented as values of the type std::size_t. Valid alignments include only those values returned by an alignof expression for the fundamental types plus an additional implementation-defined set of values, which may be empty. Every alignment value shall be a non-negative integral power of two.
So, not only are power of 2 alignments allowed, but in fact only powers of 2 alignments are allowed. The difference with C++17 is that until then, only alignments less than or equal to alignof(std::max_align_t)
(i.e. fundamental alignments) are allowed. Support for bigger alignment depends on implementation.
TL;DR Your premise is wrong. But whether std::align
works, is still implementation defined until C++17.
Upvotes: 0
Reputation: 33
Might be you look for something like this:
inline size_t AlignHi(size_t size, size_t align)
{
return (size + align - 1) & ~(align - 1);
}
inline void * Allocate(size_t size, size_t align)
{
#if defined(_MSC_VER)
return _aligned_malloc(size, align);
#elif defined(__GNUC__)
align = AlignHi(align, sizeof(void*));
size = AlignHi(size, align);
void * ptr;
int result = ::posix_memalign(&ptr, align, size);
return result ? NULL : ptr;
#else
return malloc(size);
#endif
}
inline void Free(void * p)
{
#if defined(_MSC_VER)
_aligned_free(p);
#else
free(p);
#endif
}
Upvotes: 2