What alignment does HeapAlloc use
Asked Answered
N

4

12

I'm developing a general purpose library which uses Win32's HeapAlloc

MSDN doesn't mention alignment guarantees for Win32's HeapAlloc, but I really need to know what alignment it uses, so I can avoid excessive padding.

On my machine (vista, x86), all allocations are aligned at 8 bytes. Is this true for other platforms as well?

Nodule answered 10/5, 2010 at 19:44 Comment(4)
If MSDN doesn't say it, then there is no guarantee. Even if you ask 1000 people and they all determine it's 8 bytes on their machine, that's not a guarantee, and you probably shouldn't rely on it.Parik
I'd concur with the above statment. You might want to look at blogs.msdn.com/oldnewthing/archive/2007/12/27/6873648.aspx to see if it its relevant though.Bromic
I agree, there are no guarantees, but I'm not asking for guarantees here: I'm asking for experience. I'm confident Microsoft will never decrease the alignment below 8 bytes, because double's won't be readable anymore. I'd just like to know it that's also true for the embedded platforms.Nodule
Above link from torak's comment is now devblogs.microsoft.com/oldnewthing/20071227-00/?p=24013Scorekeeper
G
3

The HeapAlloc function does not specify the alignment guarantees in the MSDN page, but I'm inclined to think that it should have the same guarantees of GlobalAlloc, which is guaranteed to return memory 8-byte aligned (although relying on undocumented features is evil); after all, it's explicitly said that Global/LocalAlloc are just wrappers around HeapAlloc (although they may discard the first n bytes to get aligned memory - but I think that it's very unlikely).

The documentation page got updated in the meantime, and it now gives a precise indication:

The alignment of memory returned by HeapAlloc is MEMORY_ALLOCATION_ALIGNMENT in WinNT.h:

#if defined(_WIN64) || defined(_M_ALPHA)
#define MEMORY_ALLOCATION_ALIGNMENT 16
#else
#define MEMORY_ALLOCATION_ALIGNMENT 8
#endif

So, it's 16 byte on 64 bit Windows, 8 on 32 bit Windows, at least on the OS versions to which the documentation refers (Windows XP onwards).

If you really want to be sure, justIn particular cases, you can also use GlobalAlloc, or even VirtualAlloc, whose granularity is the page granularity, which is usually 4 KB (IIRC), but in this case for small allocations you'll waste a lot of memory.

By the way, if you use C++ new operator, you are guaranteed to get memory aligned correctly for the type you specified: this could be the way to go. this too is not really correct; until C++17, new didn't have the necessary plumbing to deal with "over-aligned" types (i.e. types with alignment requirements greater than those for std::max_align_t, which generally is double), so in practice generally it just provided 8 or 16 byte alignment. This is fixed since C++17, where the new that is provided in the standard library should be able to deal with such types. See P0035R4, std::max_align_t, std::align_val_t for more information.

Goldia answered 10/5, 2010 at 20:52 Comment(0)
M
7

Surprisingly, Google turns up evidence that HeapAlloc is not always SSE-compliant:

HeapAlloc() has all the objects always 8-byte aligned, no matter what their size is (but not 16-byte-aligned, for SSE).

The post is from mid 2008, suggesting that recent Windows XP suffers from this bug.

See also http://support.microsoft.com/kb/286470:

The Windows heap managers (all versions) have always guaranteed that the heap allocations have a start address that is 8-byte aligned (on 64-bit platforms the alignment is 16-bytes).

Maureen answered 10/5, 2010 at 20:41 Comment(2)
Yep, SSE types live a kind of shadow existence where their alignment is rarely respected by allocation functions. I'm not sure why, but it seems to be the case on both Windows and Linux. By default, you don't get better alignment guarantees than 8 bytes.Cunnilingus
@jalf: strange! I recall in 1999 when AltiVec was introduced, Apple immediately respecified their allocation functions NewPtr and NewHandle for 16-byte alignment. It's not like you break old software that way!Maureen
G
3

The HeapAlloc function does not specify the alignment guarantees in the MSDN page, but I'm inclined to think that it should have the same guarantees of GlobalAlloc, which is guaranteed to return memory 8-byte aligned (although relying on undocumented features is evil); after all, it's explicitly said that Global/LocalAlloc are just wrappers around HeapAlloc (although they may discard the first n bytes to get aligned memory - but I think that it's very unlikely).

The documentation page got updated in the meantime, and it now gives a precise indication:

The alignment of memory returned by HeapAlloc is MEMORY_ALLOCATION_ALIGNMENT in WinNT.h:

#if defined(_WIN64) || defined(_M_ALPHA)
#define MEMORY_ALLOCATION_ALIGNMENT 16
#else
#define MEMORY_ALLOCATION_ALIGNMENT 8
#endif

So, it's 16 byte on 64 bit Windows, 8 on 32 bit Windows, at least on the OS versions to which the documentation refers (Windows XP onwards).

If you really want to be sure, justIn particular cases, you can also use GlobalAlloc, or even VirtualAlloc, whose granularity is the page granularity, which is usually 4 KB (IIRC), but in this case for small allocations you'll waste a lot of memory.

By the way, if you use C++ new operator, you are guaranteed to get memory aligned correctly for the type you specified: this could be the way to go. this too is not really correct; until C++17, new didn't have the necessary plumbing to deal with "over-aligned" types (i.e. types with alignment requirements greater than those for std::max_align_t, which generally is double), so in practice generally it just provided 8 or 16 byte alignment. This is fixed since C++17, where the new that is provided in the standard library should be able to deal with such types. See P0035R4, std::max_align_t, std::align_val_t for more information.

Goldia answered 10/5, 2010 at 20:52 Comment(0)
V
1

Amd64 heap alignment is documented here:

https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160

...stack pointer and malloc or alloca memory, which are 16-byte aligned to aid performance.

Vitamin answered 13/7, 2021 at 19:25 Comment(0)
D
0

The alignment will be such that the returned address can be cast to a pointer of any type. Otherwise you will not be able to use the memory in your application.

Delanty answered 10/5, 2010 at 20:8 Comment(4)
If I understand correctly your answer, the fact that the returned address can be casted to a pointer of any type isn't related to the alignment of the allocated memory.Goldia
@Matteo: it is related. If it was not aligned for a type T, then a cast to T* would be invalid (it'd compile, but it wouldn't be guaranteed to work). So because it can be cast to any type, it must be aligned strictly enough to work with any type.Cunnilingus
Ok, I understood your answer in another way, so what I said before do not apply; still, the OS cannot guarantee that it is aligned for any type, otherwise only a NULL pointer would be ok :) . Probably it's guaranteed to work for the biggest type which requires alignment known to people who wrote the allocator (or to the biggest type which requires alignment that the people who wrote the allocator are willing to support directly).Goldia
Unfortunately, this guarantee is not valid: on vista32, the alignment is 8 bytes, which is suitable for doubles and long longs, but it isn't suitable for the SSE intrinsic types, which require 16 byte alignment, so one needs to manually align it.Nodule

© 2022 - 2024 — McMap. All rights reserved.