Notes on (memory) alignment
When the compiler turns your program into machine code, it decides how to lay out values in memory. We say that a value in memory is 8-byte aligned if the value's memory address is a multiple of 8. The alignment is always a power of two.
The natural alignment of a type is its size in bytes. For instance, if int
is represented by 4 bytes, then its natural alignment is also 4 bytes.
Historically, misaligned reads were slower, but this is no longer true on x86 CPUs.1 2 Misaligned reads used to be illegal on ARM. Modern ARM processors allow them, but the official documentation says there is a performance penalty.3
If you write to a misaligned address, you are no longer guaranteed that the write is atomic.4
Intuition: CPU reads/writes aligned memory – 2 bytes at 2-byte alignment, 4 bytes at 4-byte alignment, etc. If your value is misaligned, the CPU has to do two memory accesses instead of one. (Caveat: Modern processors are extremely complicated and I don't know if this is literally true.)
When do I need to care?
Not usually. The compiler will ensure that types are aligned to their natural alignment, and malloc
returns a pointer that is "suitably aligned for any type that fits into the requested size or less" according to man 3 malloc
.
You can make your C structs more compact by ordering the fields in decreasing order of alignment.
struct Test1 {
char a; // 0x00
int b; // 0x04 for alignment
char c; // 0x08
// 3 bytes of padding
};
struct Test2 {
int b; // 0x00
char a; // 0x04
char c; // 0x05
// 2 bytes of padding
};
struct Test1
takes up 12 bytes while struct Test2
only needs 8.
Why not 6? Because the alignment of the struct overall is the max alignment of any field. In this case, alignof(int) == 4
, so the struct must be 4-byte aligned, and the compiler adds 2 bytes of padding at the end.
You might care if you are casting bytes read from disk or the network to a struct, because char*
has 1-byte alignment but your struct probably has stricter alignment requirements. You can ensure alignment with compiler attributes:
char buf[4096]
__attribute__((aligned(__alignof__(struct my_struct))));
On the other hand, it's safe (at least, in terms of alignment) to cast any type to char*
to get its underlying representation in bytes, since char*
has 1-byte alignment.
-
"Data alignment for speed: myth or reality?" (Daniel Lemire, 2012) ↩
-
"Test results for Intel's Sandy Bridge processor" (Agner Fog, 2011) ↩
-
"Alignment" (ARM Developer) ↩
-
"If aligned memory writes are atomic, why do we need the sync/atomic package?" (Dave Cheney, 2018) ↩