Large Struct COW optimization

Hello,

Recently I found myself forced to think about the performance issues which COW for structs might introduce in my code. To put the problem into context I will try to explain the situation in greater detail (despite some of those details are not really relevant to the problem, they will give some better sense to the context). Specifically, I have some business layer DTO entity modeled using a struct which has to be mapped to another corespoding struct model. In order to map it, the initialization needs some information contained in a different entity. Take the following example:

struct DestinationDTO {
    let id: String
}

struct MeetingDTO {
    let id: String
}

struct Meeting {
    let id: String
    let destinationId: String
    
    init(meeting: MeetingDTO, destinations: [DestinationDTO]) {
        self.id = meeting.id
        ...
    }
}

let destinations = [DestinationDTO(id: "1"), DestinationDTO(id: "2")])

let meeting = Meeting(meeting: MeetingDTO(id: "21"), destinations: destinations]

Worth mentioning is that Meeting only needs one element from destinations, based on destinationId.

Now as I understand from the Swift documentation, value types are copied when passed as function arguments which means destinations is presumably copied when passed to the Meeting initializer. I shall continue by saying that for my particular case the corresponding DestinationDTO struct is one very large. Moreover this case also occurs one level deeper, where another field of Meeting is initialized in the same fashion using a field from the corresponding (by destinationId) destination .

Nevertheless, also mentioned in the Swift documentation is:

Collections defined by the standard library like arrays, dictionaries, and strings use an optimization to reduce the performance cost of copying. Instead of making a copy immediately, these collections share the memory where the elements are stored between the original instance and any copies. If one of the copies of the collection is modified, the elements are copied just before the modification. The behavior you see in your code is always as if a copy took place immediately.

Provided the destinations collection is not modified during the initialization, can I resolve this to: the same copy of destinations is used for each initialization ? (if multiple are required at call site)

Thank you.

Yes, that should share the underlying Array and String storage.

1 Like

Hey thanks, sounds good.

This means that even though, say inside the initializer, I would do something like:

let meetingDestination = destinations.first { $0.id == destinationId }

meetingDestination still does not trigger COW as long as it is just read?

Correct. You'd need to a) mutate it in some way, while b) more than one variable was sharing the storage.

1 Like

Fair enough, thank you ! :fist_right: :fist_left: