Sign in to follow this  

Memory alignment

This topic is 3373 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working on a simple memory manager for debugging purposes. One of the things I'm doing is writing a custom ::operator new that adds an allocation header to any memory allocated on the heap. This is being done to gather metrics, but mostly to track memory leaks. However, I have some concerns about memory alignment. In C++ what I'm doing looks something like this:
void* operator new(std::size_t uiSize)
{
    std::size_t newSize = uiSize + sizeof(AllocationHeader);
    void* pMem = malloc(newSize);
    AllocationHeader* pHeader = pMem;
    // Fill out allocation header data here
    void* pObjectMemBlock = pMem + sizeof(AllocationHeader);
    return pObjectMemBlock; 
}

My concern is that the returned pObjectMemBlock may not be aligned properly, because I'm not returning the aligned pMem returned by malloc but rather an address part way into that block. Now I assume that I can solve the problem by padding AllocationHeader to a certain size. Unfortunately I'm not sure what size. I'm working on several different platforms too, so I may need to align differently for each platform. Those platforms are: *Intel Core 2 Duo under WinXP *AMD Athlon 4800+ under WinXP *Intel Celeron Dual Core under Ubuntu *Intel based MacBook under OSX These can all be considered x86 architecture, right? I've heard though that Linux machines barf on misaligned memory though. is that right? Anyway, if anyone can tell me how I should pad AllocationHeader that'd be great! Incidentally, I'm aware that x86 machines can generally handle misaligned memory at a performance cost. Although I'll only be using this code for debug builds, I'd still prefer everything be aligned correctly. Cheers!

Share this post


Link to post
Share on other sites
As you are aware, x86 handles unaligned memory accesses perfectly fine at a slight performance hit. This is true regardless of operating system.

Typically, alignment would be 8-bytes on a 32-bit architecture, and 16-bytes on a 64-bit architecture. I'd say that just aligning to a 16-byte boundary is a pretty safe bet.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
Typically, alignment would be 8-bytes on a 32-bit architecture, and 16-bytes on a 64-bit architecture. I'd say that just aligning to a 16-byte boundary is a pretty safe bet.


Just curious, where do you get that information from? I thought it was typically equal to the system's word size.

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
Just add max_alignment-the_size_of_whatever_you_add to realign it.

The problem is that malloc is often broken and doesn't provide the max alignment as it should.


I'm unfamiliar with max_alignment and can't find it in any of the standard headers. Where is it usually defined? FYI I'm using GNU compiler & libs.

Thanks!

Share this post


Link to post
Share on other sites
Funnily enough, now that I've looked more closely, sizeof(AllocationHeader) seems to already be doing the alignment work for me. No matter the size of the structure, it always returns a value that's a multiple of 8. I wonder if it would return multiples of 16 on a 64-bit architecture?

Thanks for the help guys :)

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
The problem is that malloc is often broken and doesn't provide the max alignment as it should.
On what platform? Win32 and GCC both guarantee it to be 8 bytes on 32 bit platforms and 16 bytes on 64.

You are correct that you need to handle alignment yourself. If you know that you are only going to compile a 32bit binary then you can safely add some padding to your AllocationHeader to force it to be a multiple of 8 bytes. This will keep you consistent with the usual alignment for new.

Share this post


Link to post
Share on other sites
Does your AllocationHeader have a member that is 8 bytes? That would make the compiler pad the structure. Unless one of the types in the structure change size the size should stay the same on 64-bit.

Share this post


Link to post
Share on other sites
The padding appears to occur on all platforms, but I'm using GCC on all too. So if it guarantees 8 bit alignment on 32 bit platforms then that would make sense.

I'll add padding anyway to guarantee 8 byte alignment (and 16 if I ever build for 64-bit) because I like to cover all bases. However, if I'm only building with GCC this isn't strictly necessary, right? As I said, I'll do it all the same because I prefer to write robust code that will hold up if circumstances change down the line (e.g. different compiler).

Oh and Wolfdog, at present the AllocationHeader I'm using is only 6 bytes total but gets padded to 8 all the same.

Cheers!

Share this post


Link to post
Share on other sites
Quote:
Original post by tharris997
The padding appears to occur on all platforms, but I'm using GCC on all too. So if it guarantees 8 byte alignment on 32 bit platforms then that would make sense.

I'll add padding anyway to guarantee 8 byte alignment (and 16 if I ever build for 64-bit) because I like to cover all bases. However, if I'm only building with GCC this isn't strictly necessary, right? As I said, I'll do it all the same because I prefer to write robust code that will hold up if circumstances change down the line (e.g. different compiler).


You should be good with what you mentioned earlier, but adding support for 64bit doesn't hurt.

Quote:
Oh and Wolfdog, at present the AllocationHeader I'm using is only 6 bytes total but gets padded to 8 all the same.

The compiler will pad a structure for proper alignment of its members, so you probably have a 4 byte member in there that is causing the padding to a multiple of 4. Not sure about the 8 byte padding unless you have an 8 byte member.

You can use this assertion to check the alignment:
Assert((*((int*)&pObjectMemBlock) & ~7) == *((int*)&pObjectMemBlock),"Bad alignment.");

replace the 7 with 15 for 64bit, and of course you would need a 64 bit type to hold the pointer.

Share this post


Link to post
Share on other sites
Someone correct me if I'm wrong, but I think your pointer arithmetic is a bit off.
Your current code:
    AllocationHeader* pHeader = pMem;
void* pObjectMemBlock = pMem + sizeof(AllocationHeader);
Is equivalent to:
    AllocationHeader* pHeader = pMem;
char* pBytes = (char*)pMem;
void* pObjectMemBlock = pBytes + sizeof(AllocationHeader)*sizeof(AllocationHeader);
What you really want is:
    AllocationHeader* pHeader = pMem;
//void* pObjectMemBlock = pMem + 1; << edit - meant to write pHeader here!
void* pObjectMemBlock = pHeader + 1;


[EDIT] ^^fixed my typo!

[Edited by - Hodgman on September 20, 2008 12:24:16 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
Someone correct me if I'm wrong, but I think your pointer arithmetic is a bit off.
Your current code:
    AllocationHeader* pHeader = pMem;
void* pObjectMemBlock = pMem + sizeof(AllocationHeader);
Is equivalent to:
    AllocationHeader* pHeader = pMem;
char* pBytes = (char*)pMem;
void* pObjectMemBlock = pBytes + sizeof(AllocationHeader)*sizeof(AllocationHeader);
What you really want is:
    AllocationHeader* pHeader = pMem;
void* pObjectMemBlock = pMem + 1;


I'm not sure how that is equivalent?

There is a minor mistake in my code though - I should be casting pMem as a char* to avoid void pointer arithmetic errors. Other than that though, the code seems to work correctly.

In your suggested code, why would I want to increment PMem by 1 (bytes?). The AllocationHeader has it's own unique size.

Maybe I'm missing something though...

Cheers!

Share this post


Link to post
Share on other sites
If you notice:


AllocationHeader* pHeader = pMem;
void* pObjectMemBlock = pMem + 1;




pMem is a pointer to an AllocationHeader that + 1 would increase it by the size of AllocationHeader. Your original code should not have compiled because void has no known size.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wolfdog
If you notice:

*** Source Snippet Removed ***

pMem is a pointer to an AllocationHeader that + 1 would increase it by the size of AllocationHeader. Your original code should not have compiled because void has no known size.


pMem was always a void pointer. PHeader is the AllocationHeader pointer, and pHeader + 1 would be correct.

You're right though, the typo was missing out the char* casting in order to make arithmetic on pMem work

i.e.

void* pObjectMemBlock = (char*)pMem + sizeof(AllocationHeader);

Thanks! :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Wolfdog
Whoops I miss read that I thought it was void* pObjectMemBlock = pHeader + 1; which is what I assumed Hodgman ment, even after I reposted it :)


Well my code had typos anyway. Apparently we all make mistakes ;)

Thanks guys - you've all been a great help :P

Share this post


Link to post
Share on other sites

This topic is 3373 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this