What do MemoryLayout’s size, stride and alignment mean, and why does field order matter?
Predict first
What does this print?
struct A { var flag: Bool; var big: Int64; var other: Bool }
struct B { var big: Int64; var flag: Bool; var other: Bool }
print(MemoryLayout<A>.size, MemoryLayout<A>.stride, MemoryLayout<B>.size, MemoryLayout<B>.stride)
- 10 16 10 16
- 17 24 10 16
- 17 24 17 24
Mental model. Packing a suitcase with one big item and two small ones. Put the big one in first and the small ones fill the gaps; otherwise you waste space around each.
size is the number of bytes the value itself occupies. alignment is the address multiple it must sit on (often its largest field’s size). stride is the distance between consecutive elements in an array: the size rounded up to a multiple of the alignment. So stride, not size, is what an array of these values uses per element.
Swift lays struct fields out in declaration order, inserting padding to satisfy alignment. A Bool, an Int64 and another Bool take 17 bytes of content but a stride of 24. Declaring the 8-byte field first and the small ones after it gives a size of 10 and a stride of 16, a third smaller across a large array. It rarely matters, but for millions of small structs or data you map from a file it does.
A pads each small field up to the 8-byte boundary of the field after it. B puts the two flags together after the Int64.
Common traps
- Using
sizeto allocate room for an array of values. Usestride. - Assuming the compiler reorders fields for you. It does not; the order you write is the order in memory.
They might follow up with
- When is
alignmentimportant? When reading raw memory or interoperating with C, where the wrong alignment is undefined behaviour or slow. - Do enums use spare bits? Yes. Payload enums and
Optionalof a class reuse unused bit patterns, soOptional<AnyObject>is still 8 bytes.
Related questions
- 3.4.1 What does a class instance look like in memory?
- 3.5.3 What is an Array in memory, and what is special about the empty array?
← What does a class instance look like in memory? What actually allocates on the heap in Swift, and how do you see it? →