11

In Visual Studio, when compiling 64-bit:

  • sizeof(std::max_align_t) is 8
  • __STDCPP_DEFAULT_NEW_ALIGNMENT__ is 16

So although std::max_align_t indicates that implementations of new should return pointers aligned to a multiple of 8 bytes, allocations with an alignment requirement of 16 bytes don't call the void* operator new (std::size_t count, std::align_val_t); method but call void* operator new (std::size_t count); (see https://en.cppreference.com/w/cpp/memory/new/operator_new) and expect them to return a pointer aligned on 16 bytes.

So allocating a struct defined like this:

struct alignas(16) S {double m_value;};

will call the standard operator new (without std::align_val_t argument) and expect it to be aligned on 16 bytes, while std::max_align_t only specifies that it should be aligned on 8 bytes.

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.

  • Am I missing something?
  • Is this an error in the way Visual Studio implements C++/STL?
  • Or is this an error in the C++/STL standard?
Patrick
  • 23,217
  • 12
  • 67
  • 130
  • [[basic.align\]/3](http://eel.is/c%2B%2Bdraft/basic.align#3) mentions both of these constants but I'm not sure what it implies regarding how their values relate. Since the standard gives different names to alignments greater than each of those I would presume that there is (intentionally) no guarantee that they are identical. Can't tell you why though. – Max Langhof May 16 '19 at 15:08
  • It doesn't change the result in this case, but I think you intended `alignof(std::max_align_t)`, not `sizeof(std::max_align_t)`. – walnut Dec 04 '19 at 08:09

1 Answers1

10

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I corrected a typing error in my question: allocations with an alignment requirement of 16 bytes don't call the void* operator new (std::size_t count, std::align_val_t); (I forgot the align_val_t). https://en.cppreference.com/w/cpp/memory/new/operator_new explicitly states that the new operator with the std::align_val_t argument is called when 'storage required for a single object whose alignment requirement exceeds __STDCPP_DEFAULT_NEW_ALIGNMENT__'. So this forces implementations to return pointers aligned on 16 bytes, while std::max_align_t states that it must be 8 bytes (by default). – Patrick May 16 '19 at 15:34
  • 2
    @Patrick: "*while std::max_align_t states that it must be 8 bytes.*" It states no such thing. That is simply the maximum size before the type is considered to have extended alignment. It says nothing about the alignment of pointers returned from `operator new`. – Nicol Bolas May 16 '19 at 15:37
  • you're right. Assuming that `std::max_align_t` also says something about the alignment of pointers returned by the `operator new` was wrong of me. Nevertheless, if `__STDDPP_DEFAULT_NEW_ALIGNMENT__` is 16, it means that a lot of memory is lost if you only need 8 bytes (but the same could be said for 4 bytes of course). – Patrick May 16 '19 at 15:42
  • 1
    ok, but why is it this way? Why not have new always return 8 byte alignment as well? – Joseph Garvin Jan 28 '20 at 19:59
  • 1
    @JosephGarvin: Because types that require 16-byte alignment existed over a decade before C++'s default `new` provided a mechanism to explicitly request allocations of such alignments. In order to allow users to use such types, Microsoft decided to make their default allocator always return 16-byte-aligned pointers. – Nicol Bolas Jan 28 '20 at 21:41
  • @NicolBolas and then for AVX and AVX2 everybody threw up their hands? Because there are 32-byte requiring instructions and the new alignment is still only 16... – Joseph Garvin Jan 29 '20 at 03:17
  • 1
    @JosephGarvin: It is possible to write your own allocator which returns memory of higher alignment. At some point, the default may not be good enough. Microsoft simply thought it was a good idea to make the default bigger than others have. Also, AVX was 2011, and Microsoft wasn't about to change the underlying foundation of their standard allocator like that. – Nicol Bolas Jan 29 '20 at 03:58
  • @JosephGarvin C++ allocation functions (`operator new`) typically call `malloc` internally. If the implementation of `malloc` returns a 16-byte aligned memory block, then the C++ implementation has no possibility to change it. Even when not-over-aligned types require 8-byte alignment at maximum. – Daniel Langr Feb 09 '21 at 13:37