Our Properties: Gamasutra GameCareerGuide IndieGames Indie Royale GDC IGF Game Developer Magazine GAO
My Message close
Contents
Data Alignment, Part 2: Objects on The Heap and The Stack
 
 
Printer-Friendly VersionPrinter-Friendly Version
 
Latest News
spacer View All spacer
 
February 9, 2012
 
Activision Blizzard reports better than expected 2011 thanks to MW3, Skylanders
 
DICE 2012: Putting story before gameplay 'a waste of time' says Jaffe [5]
 
What Nintendo's 2011 sales mean for Wii U, third parties [10]
spacer
Latest Jobs
spacer View All     Post a Job     RSS spacer
 
February 9, 2012
 
Warner Bros Games
Senior Software Engineer, Online - WB Games - 128905BR
 
NetherRealm Studios
Senior Software Engineer, Network - WB Games/NetherRealm Studios - 126710BR
 
NetherRealm Studios
Senior Designer - WB Games/NetherRealm Studios - 129139BR
 
CCP - North America
Lead/Sr Environment Artist
 
iWin, Inc.
Director of Development - Social Games
 
iWin, Inc.
Flash and Java Developer / Senior Developer – Social and Mobile Games
spacer
Latest Features
spacer View All spacer
 
February 9, 2012
 
arrow Principles of an Indie Game Bottom Feeder [13]
 
arrow Postmortem: CyberConnect 2's Solatorobo: Red the Hunter [1]
 
arrow Jerked Around by the Magic Circle - Clearing the Air Ten Years Later [37]
 
arrow Building the World of Reckoning [4]
 
arrow SPONSORED FEATURE: TwitchTV - How to Build Community Around Your Game in 2012 [13]
 
arrow Happy Action, Happy Developer: Tim Schafer on Reimagining Double Fine [9]
 
arrow Building an iOS Hit: Phase 1 [11]
 
arrow Postmortem: Appy Entertainment's SpellCraft School of Magic [5]
spacer
Latest Blogs
spacer View All     Post     RSS spacer
 
February 9, 2012
 
Double Fine's Kickstarter Windfall: Will Patronage Supplant Traditional Game Publishing? [4]
 
The Principles of Game Monetization
 
Did DoubleFine Just break the publishing model for good? [3]
 
The Devil Is in the Details of Action RPGs - Part One: The Logistics of Loot [4]
 
Xbox LIVE Indie Games at it Again
spacer
About
spacer Editor-In-Chief/News Director:
Kris Graft
Features Director:
Christian Nutt
Senior Contributing Editor:
Brandon Sheffield
News Editors:
Frank Cifaldi, Tom Curtis, Mike Rose, Eric Caoili, Kris Graft
Editors-At-Large:
Leigh Alexander, Chris Morris
Advertising:
Jennifer Sulik
Recruitment:
Gina Gross
 
Feature Submissions
 
Comment Guidelines
Sponsor
Features
  Data Alignment, Part 2: Objects on The Heap and The Stack
by Noel Llopis [Programming]
4 comments Share on Twitter Share on Facebook RSS
 
 
March 31, 2009 Article Start Page 1 of 3 Next
 

[Continuing his two-part article on data alignment, originally printed in Game Developer magazine, game programming veteran Llopis explains how to align objects on the heap, and use this effectively for game coding.]

Last month we looked at data alignment, how to align static variables, and how to allocate aligned memory on the heap through a simple custom allocator. It's a good start, but we need more than that to be able to use alignment effectively in a game.


This month, I'll wrap up this topic by looking at how to allocate any object on the heap with a given alignment and explore allocations on the stack.

Be warned that this is going to take some digging into the dark, dusty corners of the C++ language. So sit comfortably by the fireplace, grab your trusty copy of the C++ Standard, and let's get started.

Aligning Objects on the Heap

The main problem with aligned_malloc is that it simply allocates a block of data, and we often want to allocate full instances of classes or structs with a particular alignment.

In a pinch, we can turn to the underused and unloved placement new. Placement new works just like the regular new you're used to, but it takes an extra parameter with the memory address where the object will be instantiated. Placement new won't allocate the memory for you-it will just create an object of the right type where you tell it to and call its constructor.

Combining placement new and aligned_malloc, you could create an aligned waypoint object on the heap. See Listing 1 below.

Listing 1: Aligned Waypoint Object Created on the Heap

void* buffer = aligned_malloc(sizeof(Waypoint), 16);
Waypoint* waypoint = new (buffer) Waypoint();
//...
waypoint-> ~Waypoint();
aligned_free(buffer);

It works, but that's some ugly code, not the kind of thing you want to see all over your codebase. Not only are we responsible for keeping around the aligned pointer so we can free it ourselves, but we have to call the object's destructor by hand.

A cleaner way to do it would be to create custom operator new and operator delete functions for the class. Those operators would be responsible for doing all the bookkeeping behind the scenes: hanging onto the pointer, calling the destructor, and freeing the memory with the correct function. If we implement this approach with our Waypoint class, Listing 1 would be reduced to:

Waypoint* waypoint = new (aligned_malloc(sizeof(Waypoint), 16))) Waypoint();
//...
delete waypoint;

That code is better, but we can simplify it further. Instead of passing the pre-allocated memory, we can simply pass the desired alignment, and let the custom operator new call aligned_malloc.

To free memory correctly, the corresponding operator delete also needs to call aligned_free. The allocation code is now:

Waypoint* waypoint = new (16) Waypoint();
//...
delete waypoint;

That's much cleaner!

As we saw last month, memory allocated with aligned_malloc needs to be freed with aligned_free. Attempting to use the wrong free function on a block of memory will result in a heap corruption (and possibly some really hard to track down bugs).

That means that operator delete needs to know how the object was allocated and decide which free function to call. Since operator delete can't easily access the contents of the object it's creating, we would have to allocate extra space and set some flags around the memory that was just allocated, indicating which function was used.

Alternatively, a simple solution is to overwrite the default operator new and call aligned_malloc from it as well, but without any alignment. That way all memory is allocated through aligned_malloc and operator delete can safely call aligned_free for all objects.

Since creating all those operators is just a bunch of busy work, we can wrap them in a macro called DYNAMIC_ALIGNMENT. The advantage of the macro over a base class implementing those functions is that we can use the macro in any class or structure without requiring any inheritance or changing the data layout in any way.

Since the classes we want to align are often fairly small, low-level ones, that's a particularly important consideration. (See the source code for the full implementation).

 
Article Start Page 1 of 3 Next
 
Comments

Douglas Walker
profile image
Another approach for stack-based allocations is to do the same thing you suggest for heap allocations: allocate extra data and point to the appropriate boundary within it.

void func(float x, float y)
{
char space[sizeof(Waypoint)+15];
mystruct* myptr = ((space+15) & 15);
myptr->x = x;
myptr->y = y;
... etc ...
}

You can make this a little cleaner with a macro definition:

#define STACKALIGN(name,type,align) \
char space_##name[sizeof(type)+(align-1)]; \
type* name = (type*)((unsigned long long)(space_##name + (align-1)) & ~(align-1));

(The ## preprocessor operator is used to generate a name for the space you're allocating that is based on the variable you are declaring, so you can use more than one STACKALIGN invocation in the same function.) Then your code would look like this:

void func(float x, float y)
{
STACKALIGN(myvar, Waypoint, 16);
myvar->x = x;
myvar->y = y;
... etc ...
}

There's a minimal amount of calculation, but no cleanup. Be aware, however, that the constructor and destructor of your class will not be called using this method, so it's best used for simple structs or arrays, or for providing raw memory to a custom placement new operator.

Yet another approach is to use a localalloc function, which allocates stack space like it was heap space. That's pretty straightforward if it's available but it's very platform-specific, and you'd have to pull the same tricks as above to align the locally allocated memory.

Douglas Walker
profile image
oops.... that should have been ((space+15) & ~15) in the first example, of course. I got it right in the macro.

Bob Stevens
profile image
You touched on this, but you shouldn't plan to align objects allocated with new[] even if they're POD and properly padded. You can't do it on 33% (at least) of the current gen home consoles, unless you're using a custom operator new[] and delete[]. It's best to avoid relying on this behavior.

Kevin Weatherman
profile image
Great article Noel!
And thanks for the nice stack alignment technique Douglas.

A bonus if using C++ you can easily turn this macro from pointer back to the dot operator using by reference instead.
Then one can switch to and from the macro way easier.

Change the 2nd line to:
type &name = *((type*)((unsigned long long)(space_##name + (align-1)) & ~(align-1)));

We are back to the default '.' like this:
myvar.x = x;
myvar.y = y;


none
 
Comment:
 




UBM Techweb
Game Network
Game Developers Conference | GDC Europe | GDC Online | GDC China | Gamasutra | Game Developer Magazine | Game Advertising Online
Game Career Guide | Independent Games Festival | Indie Royale | IndieGames

Other UBM TechWeb Networks
Business Technology | Business Technology Events | Telecommunications & Communications Providers

Privacy Policy | Terms of Service | Contact Us | Copyright © UBM TechWeb, All Rights Reserved.