Saturday, November 2, 2019

Efficiently Mutating Nested Swift Data Structures

Radek Pietruszewski:

That feel when you’re profiling a React Native app, and you’re sure JavaScript is the source of your problems… but no, it’s ridiculously misoptimized Swift doing… well… nothing much for two minutes.

[…]

Code does something that seems completely reasonable, but ends up creating a new copy of a large Set 10,000s times. Not entirely sure if Swift is supposed to be smart enough to figure out that it can safely mutate it, or it’s programmer’s responsibility.

The intention is to insert to a Set that’s in a Dictionary that’s a mutable variable on a class

Joe Groff:

Older versions could not modify values in dictionaries without copying them, but 5.0 and later should be able to.

Karoy Lorentey:

Yep, Array & Dictionary both support in-place mutations. For Dictionary, the recommended way is to use the defaulting subscript. E.g., this won’t make a CoW copy:

sets[key, default: Set()].insert(foo)

But this will:

var tmp = sets[key]
tmp.insert(foo)
sets[key] = tmp

The Dictionary.Values collection also supports this, and it may come handy if you need to distinguish between existing and new keys:

if let i = sets.index(forKey: key) {
  sets.values[i].insert(foo)
} else {
  fatalError("Unknown key \(key)")
}

I’ve written about this before, but I think it’s worth re-emphasizing how this works. It’s counterintuitive and unusual in programming for introducing a temporary variable, as people often do when debugging, to radically change the behavior of the code. Copy-on-write is a leaky abstraction.

Previously:

Update (2019-11-02): See also: Karoy Lorentey.

2 Comments RSS · Twitter

Agreed, COW does not quite accomplish its goal of freeing the user from having to think of the memory implications of manipulating arrays as value types. It's a very primitive optimization, but at least it's fairly easy to reason about once you are aware. Now that swift 5 does the right thing with nested collections, it behaves predictably and it can be managed.

Most languages don't even try, and arrays are always references, which brings its own set of issues...

[…] Efficiently Mutating Nested Swift Data Structures […]

Leave a Comment