Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do c++ aligned array allocation?

I'd like to modify an array allocation:

 float * a = new float[n] ;

to use an aligned allocator. I was inclined to try to use placement new and posix_memalign (or the new c++11 equivalent), but see that placement new with arrays is problematic with array allocations, because the compiler may need to have additional storage for count or other metadata.

I tried:

int main()
{
   float * a = new alignas(16) float[3] ;

   a[2] = 0.0 ;

   return a[2] ;
}

but the compiler seems to indicate that the alignas is ignored:

$ g++ -std=c++11 t.cc -Werror
t.cc: In function ‘int main()’:
t.cc:4:39: error: attribute ignored [-Werror=attributes]
    float * a = new alignas(16) float[3] ;
                                       ^
t.cc:4:39: note: an attribute that appertains to a type-specifier is ignored

It looks like the proper way to use alignas is in a structure declaration declare a structure with alignas, but that will only work with a fixed size.

There is also a aligned_storage template, but I think that will also only work with fixed sizes.

Is there any standard way to do an aligned array allocation that will invoke the constructor on all the elements?

like image 270
Peeter Joot Avatar asked Mar 06 '16 20:03

Peeter Joot


People also ask

How does aligned_ alloc work?

The aligned_alloc function allocates space for an object whose alignment is specified by alignment, whose size is specified by size, and whose value is indeterminate.

Why aligned_ alloc?

Aligned mallocIt allows the user to specify the alignment, which is required for some kinds of operations. For example, DMA (I/O via direct memory access) might require a buffer with 512 byte alignment, aligned to cache lines etc.

Is malloc always aligned?

Since malloc (or another dynamic memory allocator) is not necessarily guaranteed to align memory as we require, we'll need to perform two extra steps: Request extra bytes so we can returned an aligned address.

What is alignment in C programming?

Data alignment: Data alignment means putting the data in memory at an address equal to some multiple of the word size. This increases the performance of the system due to the way the CPU handles memory.


2 Answers

As other people said, overaligned types are not required to be supported. Check your compiler documentation before using it.

You can try to solve your problem using one of the following approaches:

1) Overallocate your array (by (desired aligment / sizeof element) - 1) and use std::align. A link to libstdc++ implementation.

2) declare a struct containing array of desired aligment / sizeof element elements and aligned by desired aligment. It should give you compact representation in memory if you use array of such structs, but you will not be able to use normal array notation or pointer arithmetics (as it (a) undefined behaviour, (b) there is a small chance that they will not be placed as you want)

3) Write your own aligned allocation function. Notice that you can add your own versions of operator new and delete.

namespace my
{
    struct aligned_allocator_tag {};
    aligned_allocator_tag aligned;
}

void* operator new( std::size_t count, my::aligned_allocator_tag, std::size_t aligment);
void* operator new[]( std::size_t count, my::aligned_allocator_tag, std::size_t aligment)
{
    return ::operator new(count, my::aligned, aligment);
}
//Usage
foo* c = new(my::aligned, 16) foo[20];

You will need to allocate memory, reserve enough space to store original pointer (returned by malloc/whatever) or amount of bytes pointer was displaced, so subsequent delete will free corect pointer, align pointer to needed size and return it.

Here is an answer, and another one, which shows how to align memory.

Notice that both of these answers uses implementation-defined behaviour of doing bitwise arithmetic on pointers converted to integers and converting them back. The only really completely standard way would be to cast memory to char* and add difference between its value and next aligned address.

If you can use some nonstandard memory allocation functions, you can wrap them into custom operator new too.

like image 90
Revolver_Ocelot Avatar answered Nov 04 '22 23:11

Revolver_Ocelot


Basically, you're stuck because, in [expr.new]:

It is implementation-defined whether over-aligned types are supported.

There is a proposal to support this better. Until then, if you want to do what you're trying to do, you'll have to use aligned_alloc instead of new.


If you stick your array in a struct:

struct S {
    alignas(16) float _[3];
};

then new S will give you the right alignment for _, though not necessarily for S itself. That may suffice. If it doesn't, then you can overload operator new() and operator delete() on S itself to guarantee the correct behavior.

like image 29
Barry Avatar answered Nov 04 '22 23:11

Barry