Does the Alignas Specifier Work with 'New'

Does the alignas specifier work with 'new'?

Before C++17, if your type's alignment is not over-aligned, then yes, the default new will work. "Over-aligned" means that the alignment you specify in alignas is greater than alignof(std::max_align_t). The default new will work with non-over-aligned types more or less by accident; the default memory allocator will always allocate memory with an alignment equal to alignof(std::max_align_t).

If your type's alignment is over-aligned however, your out of luck. Neither the default new, nor any global new operator you write, will be able to even know the alignment required of the type, let alone allocate memory appropriate to it. The only way to help this case is to overload the class's operator new, which will be able to query the class's alignment with alignof.

Of course, this won't be useful if that class is used as the member of another class. Not unless that other class also overloads operator new. So something as simple as new pair<over_aligned, int>() won't work.

C++17 adds a number of memory allocators which are given the alignment of the type being used. These allocators are used specifically for over-aligned types (or more specifically, new-extended over-aligned types). So new pair<over_aligned, int>() will work in C++17.

Of course, this only works to the extent that the allocator handles over-aligned types.

Why does the alignas specifier throw an error on Clang?

Well, the grammar you cited states that an attribute-specifier-seq that occurs in a type-specifier-seq has to occur after all type-specifiers. Therefore, you'd have to put it here in order to have the attributes appertain to the preceding int according to [dcl.type]/1:

new int attribute-specifier-seq [2];

You'd have to put it here in order to have the attributes appertain to the array type according to [expr.new]/6:

new int [2] attribute-specifier-seq;

You could also combine them: you can have different attributes for the array type and for the element type. But I don't think the grammar allows you to place the attributes before the int.

As for the specific issue of an alignment specifier: the standard neither explicitly permits nor explicitly forbids applying an alignment specifier to a type-specifier or type-id ([dcl.align]/1). Clang seems to interpret the standard as forbidding it. GCC seems to interpret the standard as allowing it; however, since the standard does not specify any semantics for the alignment specifier when it appertains to a type, a warning is issued. Consider:

struct S {
int x alignas(16);
int alignas(16) y;
};

Here, S::x will have an alignment requirement of 16 but the alignment specifier for S::y is meaningless. So if you write int alignas(16) y, it makes sense for GCC to warn you so you realize you put it in the wrong place.

To force the allocated memory to be aligned on a 16-byte boundary, use the placement syntax to explicitly call the overload of operator new that takes an alignment argument:

int* x = new (std::align_val_t(16)) int [2];

Memory alignment : how to use alignof / alignas?

Alignment is a restriction on which memory positions a value's first byte can be stored. (It is needed to improve performance on processors and to permit use of certain instructions that works only on data with particular alignment, for example SSE need to be aligned to 16 bytes, while AVX to 32 bytes.)

Alignment of 16 means that memory addresses that are a multiple of 16 are the only valid addresses.

alignas

force alignment to the required number of bytes. You can only align to powers of 2: 1, 2, 4, 8, 16, 32, 64, 128, ...

#include <cstdlib>
#include <iostream>

int main() {
alignas(16) int a[4];
alignas(1024) int b[4];
printf("%p\n", a);
printf("%p", b);
}

example output:

0xbfa493e0
0xbfa49000 // note how many more "zeros" now.
// binary equivalent
1011 1111 1010 0100 1001 0011 1110 0000
1011 1111 1010 0100 1001 0000 0000 0000 // every zero is just a extra power of 2

the other keyword

alignof

is very convenient, you cannot do something like

int a[4];
assert(a % 16 == 0); // check if alignment is to 16 bytes: WRONG compiler error

but you can do

assert(alignof(a) == 16);
assert(alignof(b) == 1024);

note that in reality this is more strict than a simple "%" (modulus) operation. In fact we know that something aligned to 1024 bytes is necessarily aligned to 1, 2, 4, 8 bytes but

 assert(alignof(b) == 32); // fail.

So to be more precise, "alignof" returns the greatest power of 2 to wich something is aligned.

Also alignof is a nice way to know in advance minimum alignment requirement for basic datatypes (it will probably return 1 for chars, 4 for float etc.).

Still legal:

alignas(alignof(float)) float SqDistance;

Something with an alignment of 16 then will be placed on the next available address that is a multiple of 16 (there may be a implicit padding from last used address).

Practical use cases for alignof and alignas C++ keywords

A common use case for the alignas specifier is for the scenario where you want to pass multiple objects between different threads through a queue (e.g., an event or task queue) while avoiding false sharing. False sharing will result from having multiple threads competing for the same cache line when they are actually accessing different objects. It is usually undesirable due to performance degradation.

For example – assuming that the cache line size is 64 bytes – given the following Event class:

struct Event {
int event_type_;
};

The alignment of Event will correspond to the alignment of its data member, event_type_. Assuming that the alignment of int is 4 bytes (i.e., alignof(int) evaluates to 4), then up to 16 Event objects can fit into a single cache line. So, if you have a queue like:

std::queue<Event> eventQueue;

Where one thread pushes events into the back of the queue, and another thread pulls events from the front, we may have both threads competing for the same cache line. However, by properly using the alignas specifier on Event:

struct alignas(64) Event {
int event_type_;
};

This way, an Event object will always be aligned on a cache line boundary so that a cache line will contain an Event object at most. Therefore two or more threads will never be competing for the same cache line when accessing distinct Event objects (if multiple threads are accessing the same Event object, they will obviously compete for the same cache line).

What is the correct usage/syntax for the c++17 alignas() specifier for dynamically allocated arrays of fundamental types?

While C++17 does have the means for operator new to be given an alignment for the memory it allocates, there is no mechanism in C++ to specify the alignment for memory allocated by a new expression outside of the alignment of the type being allocated. That is, if you perform a new T or new T[], the alignment of the allocated pointer will be alignof(T). C++17 added alignment-based operator new allocators, which allows them to support over-aligned types.

This is fine if you have control over T and can specify its alignment at definition time via alignas. But if you're using someone else's type or a fundamental type like double, you can't change the alignment of those types. So there's no way to directly use a new expression to allocate such memory.

You will have to use ::operator new directly to allocate sufficient memory at the desired alignment, and then use placement-new to actually create the objects there. Of course, placement-new on arrays has a number of issues.

I suspect this compiles only because alignas is considered an attribute and it is grammatically legal to shove an attribute before the [] in a new expression. It isn't intended to actually work, as there is no statement in the section on new expressions that allows it to get the alignment of the allocation from anything other than alignof(T).

Where can I use alignas() in C++11?

You cannot apply an alignment to a typedef. In the C++ model of alignment specifiers, the alignment is an inseparable part of the type itself, and a typedef does not create a new type (it only provides a new name for an existing type) so it is not meaningful to apply an alignment specifier in a typedef declaration.

From [dcl.align] (7.6.2)p1:

An alignment-specifier may be applied to a variable or to a class data member [...]. An alignment-specifier may also be applied to the declaration or definition of a class (in an elaborated-type-specifier (7.1.6.3) or class-head (Clause 9), respectively) and to the declaration or definition of an enumeration (in an opaque-enum-declaration
or enum-head, respectively (7.2)).

These are the only places where the standard says an alignment-specifier (alignas(...)) may be applied. Note that this does not include typedef declarations nor alias-declarations.

Per [dcl.attr.grammar] (7.6.1)p4:

If an attribute-specifier-seq that appertains to some entity or statement contains an attribute that is not allowed to apply to that entity or statement, the program is ill-formed.

This wording was intended to apply to alignas as well as the other forms of attribute that may appear within an attribute-specifier-seq, but was not correctly updated when alignment switched from being a "real" attribute to being a different kind of attribute-specifier-seq.

So: your example code using alignas is supposed to be ill-formed. The C++ standard does not currently explicitly say this, but it also does not permit the usage, so instead it currently would result in undefined behavior (because the standard does not define any behavior for it).

alignas specifier vs __attribute__(aligned), c++11

It seems from the GCC support status, alignment support is not fully supported in gcc 4.7, but it is for gcc 4.8. alignas is also listed as a newly supported feature from the 4.8 release page.

Also, from the alignment support proposal (3.11):

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

An extended alignment is represented by an alignment greater than
alignof(std::max_align_t). It is implementation-defined whether any extended
alignments are supported and the contexts in which they are supported (7.1.6). A type
having an extended alignment requirement is an over-aligned type.

And from the same document (7.1.6):

if the constant expression evaluates to an extended alignment and the implementation
does not support that alignment in the context of the declaration, the program is illformed

That might be part of the answer too. I don't have access to the full standard at the moment, someone should be able to confirm this.

As for the difference between __attribute__(aligned) and alignas, i don't think they are semantically different, but one is just a compiler extension while the other is fully defined by the standard.

To answer your last question, alignas is only defined for:

alignas ( constant-expression ) 
alignas ( type-id )

C++ class specifier alignas option via template

You can do it like this:

template <size_t alignment = 0>
class alignas(alignment) C {
// ...
};

Now C<> will have the default alignment for its definition (since alignas(0) is ignored) while you could use e.g. C<16> to force an alignment of 16.

Why does the C++ standard allow std::max_align_t and __STDCPP_DEFAULT_NEW_ALIGNMENT__ to be inconsistent?

There are two layers of over-aligned types in C++17: extended and new-extended. std::max_align_t defines the largest alignment that is not extended, and __STDCPP_DEFAULT_NEW_ALIGNMENT__ defines the largest alignment that is not new-extended.

New-extended alignment, as the name suggests, is about the alignment of things you allocate with new.

Basically, the regular operator new will return memory suitable for any object up to the new-extended alignment size. Any greater alignment prefers the use of operator new overloads that specify the alignment of the type being created. And of course, are conditionally supported just like over-aligned types in general. The same goes for the operator delete calls to destroy the memory associated with such types.

What Visual Studio is saying is that the maximum alignment that is not considered over-aligned is 8-byte, but the alignment for memory allocated by operator new is 16-byte.


This means that when overruling the new operators, you are forced to align everything on at least 16 bytes, even if 8 bytes would be sufficient.

Essentially, yes. There's no way to request that the implementation tell you what alignment is being requested with the raw operator new/delete overloads.

Now, you can, on an object-by-object basis, overload that object's operator new to invoke the alignment-specific operator new directly. But you can't make the compiler do so.



Related Topics



Leave a reply



Submit