Swift MemoryLayout

Viewed 37

I have a struct:

struct ShortResume {
    let age: Int32
    let hasVehicle: Bool
}

When I do print(MemoryLayout<ShortResume>.size), it prints out 5, that is correct: Int32 occupies 4 bytes and Bool occupies 1 byte.

Then I change my struct to this:

struct ShortResume {
    let age: Int32?
    let hasVehicle: Bool
}

it prints out 6, that is also correct, because optional occupies 1 byte in memory.

However, when I change my struct to this:

struct ShortResume {
    let age: Int32?
    let hasVehicle: Bool?
}

it prints out 6 again, but it must be 7, because there are two optionals now.

Why? Maybe I'm missing something?

1 Answers

If you look at

MemoryLayout<Bool?>.size

You'll see that it is actually 1. But

MemoryLayout<Int32?>.size

is 5 as expected.

You may know that Optional is an enum declared like this:

public enum Optional<Wrapped> {
    case none
    case some(Wrapped)

    // ...
}

So the memory needed for a T? is the memory needed for T, plus one flag bit to store whether it is none or some. Therefore, one byte is theoretically more than necessary to store a Bool?.

And it turns out that Swift is actually really smart - in the current implementation, this flag can go in any unused place in the layout of T, which is why the flag and the value of the Bool are in the same byte.

These are some of my experiments:

struct A {
    let w: Bool
    let x: Int8
    let y: Int8
    let z: Int8
}

MemoryLayout<A?>.size // 4, that flag must be in the first byte

struct B {
    let w: Int8
    let x: Bool
    let y: Int8
    let z: Int8
}

MemoryLayout<B?>.size // 4, that flag must be in the second byte

struct C {
    let w: Int8
    let x: Int8
    let y: Bool
    let z: Int8
}

MemoryLayout<C?>.size // 4, that flag must be in the third byte

struct D {
    let w: Int8
    let x: Int8
    let y: Int8
    let z: Bool
}

MemoryLayout<D?>.size // 4, that flag must be in the last byte

struct E {
    let w: Int8
    let x: Int8
    let y: Int8
    let z: Int8
}

MemoryLayout<E?>.size // 5, that flag must be in the extra byte
Related