3

Taking Windows as example that __STDCPP_DEFAULT_NEW_ALIGNMENT__ is 16. When I override new operator, compiler calls non-alignment version new. This indicates that compiler assumes new operator should allocate memory align to 16. (Please correct me if I already wrong).

However __STDCPP_DEFAULT_NEW_ALIGNMENT__ appears since C++17? I am wonderring what's the old age before C++17. Do you align the memory to specific bytes when overrides new operator?

I am working on a memory allocator which have a pre-allocated memory block when initializes. In theory, I can return memory with any alignment. But it definitely is ill-form when aligns to 3 when request to new an integer. So I set the minimum alignment as __STDCPP_DEFAULT_NEW_ALIGNMENT__. But I am going to work on an old project whose compiler is C++ 14. What alignment should I apply to my allocator?

The second question in my mind for a while that why __STDCPP_DEFAULT_NEW_ALIGNMENT__ is 16 on windows? Why it cannot be equal to std::max_align? I have ever seen some platform is 32 even. But why? Some explaination tells that some instructions requires to align to 16 or 32. Can you give an example?

Tinggo
  • 1,127
  • 1
  • 9
  • 18

2 Answers2

0

Since C++17, memory allocation functions guarantee that the returned pointer will be:

suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement.

This means, that the returned memory block will be aligned to at least alignof(std::max_align_t) bytes.

However, it also may be aligned more strictly. Why? Because C++ implementations need to use internally some system allocation mechanisms, such as malloc. If malloc returns a 16-byte aligned pointer, a C++ implementation has no power to change it. In that case, __STDCPP_DEFAULT_NEW_ALIGNMENT__ will be 16.

But the fundamental alignment does not necessarily be 16. It may be, for example, 8. IIRC, this is the case of 64-bit Windows with MSVC, where long double is 64-bit (same as double). Then, __STDCPP_DEFAULT_NEW_ALIGNMENT__ is 16 and, therefore, different than alignof(std::max_align_t), which is 8.

Live demo: https://godbolt.org/z/M1c64f

What alignment should I apply to my allocator?

You should apply alignof(std::max_align_t). Since C++17, this will guarantee support for all data types with at most fundamental alignment requirements (same as operator new).

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • 1. sizeof(long double) is 8. It should align to 8. Why it is related to 16 that __STDCPP_DEFAULT_NEW_ALIGNMENT__ points to? 2. std::max_align_t appears in C++11, what's the life before C++11 and what the aligment should be followed? 3. Why malloc returns 16 and what rules it? Any example can tell what causes system has to align to 16 internally? Any system is welcomed, not limited to windows. – Tinggo Feb 10 '21 at 03:49
  • @Tinggo Linux/x86_64, `long double` is 80-bit a needs to be 16-byte aligned. That's the difference between Windows. Before C++11? Honestly, I don't know. I have the C++11 Standard available, but not C++98/03. – Daniel Langr Feb 10 '21 at 04:23
  • _"Why malloc returns 16 and what rules it?"_ Have you tried to use Google? First result: [Why is dynamically allocated memory always 16 bytes aligned?](https://stackoverflow.com/q/59098246/580083) Another: [how does malloc understand alignment?](https://stackoverflow.com/q/8752546/580083). And many more... – Daniel Langr Feb 10 '21 at 04:32
  • The first linkage helps and knows 16 bytes may be the must have requirement on certain platform. If we have such platform and alignof(max_align_t) returns 8. If we uses C++14 and you tells I should apply alignof(std::max_align_t) this means 8 bytes alignment memory is returned in my allocator. Does it break the hardware 16 bytes alignment mentioned above? What do I miss? – Tinggo Feb 10 '21 at 05:56
  • 1
    @Tinggo Not from the perspective of C++11 and C++14. If one code with respect to these standards, they simply cannot count on any more strict alignment than the fundamental alignment (provided by `alignof(std::max_align_t)`). If they need higher alignment, they must use some non-C++ functions such as `posix_memalign` etc. If you have an architecture where 16-byte alignment is a "must-have", then the fundamental alignment will (I believe) be 16: https://godbolt.org/z/Eb186s6. – Daniel Langr Feb 10 '21 at 08:43
0

The fundamental alignment is represented by alignof(std::max_align_t). It means that any supported alignment is less than this value. std::max_align_t was introduced by C++11. It means that any type of alignment requirement can be met as long as alignof(std::max_align_t) is followed.

The introduction of __STDCPP_DEFAULT_NEW_ALIGNMENT__ in C++17 is accompanied by the introduction of operator new(std::size_t count, std::align_val_tal). In order to distinguish what alignment requirements will use the align version of operator new, and what alignment requirements will use the non-align version of new, __STDCPP_DEFAULT_NEW_ALIGNMENT__ gives a clear definition. Any alignment requirements greater than this value will use the align version of new. In addition, all non-align versions of new are used.

So since C++17 introduced key points When overloading operator new, if the non-aligned version of operator new overloaded version is called, it means that the alignment requirement of this allocation is less than or equal to __STDCPP_DEFAULT_NEW_ALIGNMENT__, but you still don’t know whether the allocation comes from new char or new short or new long. According to the classic solution, set the alignment to the maximum alignment. So at least this value is greater than or equal to alignof(std::max_align_t). Take MSVC as an example, alignof(std::max_align_t) is 8, then this value must be greater than or equal to 8. However, if the alignment requirement is 16, the non-aligned version of operator new will also be called, and the 8-alignment will not meet the requirement. So this value must be promoted to __STDCPP_DEFAULT_NEW_ALIGNMENT__ in order to satisfy any alignment requirements for calling the non-align version new.

Why doesn't the compiler set __STDCPP_DEFAULT_NEW_ALIGNMENT__ to alignof(std::max_align_t)? This is determined by the hardware requirements and the implementation of the compiler. Each compiler will set up according to their actual needs, we just need to follow the standards. We can also study why MSVC is 16, but remember that other platforms will have another value.

So before C++17 and after C++11, how can we align without __STDCPP_DEFAULT_NEW_ALIGNMENT__? Note: Before C++17, there was no __STDCPP_DEFAULT_NEW_ALIGNMENT__ nor operator new(std::size_t count, std::align_val_t al). At this time, all operator new will call the non-align version. Then this version of the operator implementation must be the largest case, because the actual alignment requirements of the actual allocation type cannot be known. We just need to follow alignof(std::max_align_of). A paradoxical problem is that I just used __STDCPP_DEFAULT_NEW_ALIGNMENT__ with C++17 on my local Windows. I already know that the hardware and compiler requirements of my platform are 16. Then when I switch to C++11 compilation, shall I still use 16? The answer is no, because C++11 does not have any standard that tells us something like __STDCPP_DEFAULT_NEW_ALIGNMENT__, so its compiler will not assume that its operator new will be aligned according to 16. We only need to follow the alignment requirement alignof(std::max_align_t) established by the current standard.

What about C++98 era that before C++11, how should overloaded operator new be aligned? Because there is no standard to regulate a unified place for obtaining alignment information, then application programmers can only restrict themselves according to the actual situation of their own platforms.

Tinggo
  • 1,127
  • 1
  • 9
  • 18