I have been playing God of War recently (also known as Dad of Boy) and it is amazing. Out of curiosity I looked at what it would take to be a gameplay programmer (I am not planning to move to the states, but you know, you never know what the future may hold) and once again it said “expert C++” or something along those lines. Although I live in C# Unity land most days, my C++ is pretty good, however I would hardly call it expert. I thought I should brush up on some of the more slightly more advanced parts that I don’t usually run into in C#/Unity programming day to day.
The first of these tips and tricks is C++ memory layout, most importantly the order of member declarations in a struct.
Lets look at an example.
If we calculate the size of each member using size of:
- Char1 – 1
- Number1 – 4
- Char2 – 1
- Number2 – 4
And if we do a sizeof of the whole struct we get is 16. Hang on, our calculations don’t quite add up here. 1+4+1+4 = 10. The structure is 6 bytes longer than the sum of its members. Huh, why? Maybe if we change the order this will change things.
The we do a size of again. This time the size of shows the struct is taking up 12 bytes. Moving the Char2 declaration caused the struct to shrink by 4 bytes.
Well the hardware here is optimized to read data from memory addresses which are multiples of the data size. Ints are 4 bytes and are thus read from addresses that are multiples of 4 So ints would be read from addresses such as 0, 4, 8, etc. a char on the other hand is 1 byte and are thus read from addresses 0,1,2,3, etc.
The problem is, C++ 11 standard states that data members are allocated in memory the same order they are declared assuming they have the same access specifier. In a struct all members are public causing the data to be laid out in the order defined by the compiler.
Essentially what happens is the data gets padded when allocated. The following diagram shows what happens.
The compiler starts with address 0, allocating the byte for Char1. As the following addresses are not multiples of 4 they are skipped. They are not suitable to put an int32 in. Therefore Number1 gets allocated to address 4 to 7. This causes padding in the struct and thus the struct gets larger. If we take a look at the rearranged struct:
The padding is a lot better on this struct as the members are aligned in better addresses.
In conclusion, the layout of C++ objects do matter. If you pay attention to the location of each member you can make your structs smaller and leave less of a memory footprint. Neat huh?