Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is my struct too large?

We're encouraged to use struct over class in Swift.

This is because

  1. The compiler can do a lot of optimizations
  2. Instances are created on the stack which is a lot more performant than malloc/free calls

The downside to struct variables is that they are copied each time when returning from or assigned to a function. Obviously, this can become a bottleneck too.

E.g. imagine a 4x4 matrix. 16 Float values would have to be copied on every assign/return which would be 1'024 bits on a 64 bit system.

One way you can avoid this is using inout when passing variables to functions, which is basically Swifts way of creating a pointer. But then we're also discouraged from using inout.

So to my question:
How should I handle large, immutable data structures in Swift?
Do I have to worry creating a large struct with many members?
If yes, when am I crossing the line?

like image 382
IluTov Avatar asked May 31 '15 17:05

IluTov


2 Answers

This accepted answer is not entirely answering the question you had: Swift always copies structs. The trick that Array/Dictionary/String/etc do is that they are just wrappers around classes (which contain the actual stored properties). That way sizeof(Array) is just the size of the pointer to that class (MemoryLayout<Array<String>>.stride == MemoryLayout<UnsafeRawPointer>.stride)

If you have a really big struct, you might want to consider wrapping its stored properties in a class for efficient passing around as arguments, and checking isUniquelyReferenced before mutating to give COW semantics.

Structs have other efficiency benefits: they don't need reference-counting and can be decomposed by the optimiser.

In Swift, values keep a unique copy of their data. There are several advantages to using value-types, like ensuring that values have independent state. When we copy values (the effect of assignment, initialization, and argument passing) the program will create a new copy of the value. For some large values these copies could be time consuming and hurt the performance of the program.

https://github.com/apple/swift/blob/master/docs/OptimizationTips.rst#the-cost-of-large-swift-values

Also the section on container types:

Keep in mind that there is a trade-off between using large value types and using reference types. In certain cases, the overhead of copying and moving around large value types will outweigh the cost of removing the bridging and retain/release overhead.

like image 180
karwag Avatar answered Nov 10 '22 18:11

karwag


From the very bottom of this page from the Swift Reference:

NOTE

The description above refers to the “copying” of strings, arrays, and dictionaries. The behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.

I hope this answers your question, also if you want to be sure that an array doesn't get copied, you can always declare the parameter as inout, and pass it with &array into the function.

Also classes add a lot of overhead and should only be used if you really must have a reference to the same object.

Examples for structs:

  • Timezone
  • Latitude/Longitude
  • Size/Weight

Examples for classes:

  • Person
  • A View
like image 37
Kametrixom Avatar answered Nov 10 '22 18:11

Kametrixom