Swift Optimization Tips and Reference Counting
Apple (via @objctoswift):
If you need an array of reference types and the array does not need to be bridged to
NSArray
, useContiguousArray
instead ofArray
[…][…]
Sometimes COW can introduce additional unexpected copies if the user is not careful. An example of this is attempting to perform mutation via object-reassignment in functions. In Swift, all parameters are passed in at +1, i.e. the parameters are retained before a callsite, and then are released at the end of the callee. This means that if one writes a function like the following […]
a
may be copied despite the version ofa
without one appended to it has no uses afterappend_one
due to the assignment. This can be avoided through the usage ofinout
parameters […][…]
The compiler can only specialize generic code if the call site and the callee function are located in the same compilation unit. One trick that we can use to allow compiler to optimize the callee function is to write code that performs a type check in the same compilation unit as the callee function. The code behind the type check then re-dispatches the call to the generic function - but this time it has the type information.
[…]
Swift classes are always reference counted. The swift compiler inserts code that increments the reference count every time the object is accessed. For example, consider the problem of scanning a linked list that’s implemented using classes. Scanning the list is done by moving a reference from one node to the next:
elem = elem.next
. Every time we move the reference swift will increment the reference count of thenext
object and decrement the reference count of the previous object. These reference count operations are expensive and unavoidable when using Swift classes. […] In performance-critical code you can use choose to use unmanaged references. TheUnmanaged<T>
structure allows developers to disable automatic reference counting for a specific reference.
When compiling with ARC, method arguments often appear to be retained at the beginning of the method and released at the end. This retain/release pair seems superfluous, and contradicts the idea that ARC “produces the code you would have written anyway”. Nobody in those dark, pre-ARC days performed an extra retain/release on all method arguments just to be on the safe side, did they?
When the compiler doesn’t know anything about the memory management behavior of a function or method (and this happens a lot), then the compiler must assume:
- That the function or method might completely rearrange or replace the entire object graph of the application (it probably won’t, but it could).
- That the caller might be manual reference counted code, and therefore the lifetime of passed in parameters is not realistically knowable.
Given #1 and #2; and given that ARC must never allow an object to be prematurely deallocated, then these two assumptions force the compiler to retain passed in objects more often than not.