Understanding Swift Performance

Understanding Swift Performance

Understand the implementation to understand performance

Dimensions of Performance

  1. When you are building an abstraction and choosing an abstraction mechanism you should be asking yourself, “Is my instance going to be allocated on the stack or the heap?” Allocation (Heap-or-Stack)
  2. When I pass this instance around, how much reference counting overhead am I going to occur? Reference Counting(Less-or-More)
  3. When I call a method on this instance, is it going to be statically or dynamically dispatched? Method Dispatch(Static or Dynamic)

If we want to write fast Swift code, we’re going to need to avoid paying for dynamism and runtime that we are not taking advantage of. And we’re going to need to learn when and how we can trade between these 3 different dimensions for better performance.

Swift allocates and deallocates the memory on your behalf, some of that memory allocates in the stack, some of them in the heap.
– The stack is a really simple data structure, you can push onto the end of the stack and pop off the end from the stack. Because you can only add or remove to the end of the stack, we can implement push and pop just by keeping a pointer to the end of the stack (a.k.a. Stack Pointer).

Allocation

  • Stack
    • Decrement stack pointer to allocate (When we call into a function, we can allocate that memory that we need just trivially decrementing the stack pointer to make space.)
    • Increment stack pointer to deallocate. (Then after executing the function, we deallocate that memory just by incrementing the pointer back up to where it was before we called the function)
    • Allocating and deallocating from the stack is fast. It is literally the cost of assigning an integer.
  • Heap
    • Advanced Data Structure (The Heap is more dynamic but less efficient than the Stack. It lets you do things that Stack can’t, like, allocate the memory with a dynamic lifetime. But, that requires a more advanced data structure.)
    • Search for unused block of memory to allocate
    • Reinsert block of memory to deallocate
    • Heap costs more than just assigning an integer like Stack did.
    • Thread safety overhead. (Because multiple threads can be allocating memory on the heap at the same time, the heap needs to protect its integrity by using locking or other synchronization mechanisms.)
  • Examples:
    • There is a struct Point. Whenever I create an instance of Point, since it is a value type, I create it via Stack (Value Semantics).
    • There is a class Point. Whenever I create an instance of Point, since it is a reference type, I create it via Heap, locks the heap creates it which might lead to an unintended share of state (Reference Semantics). Deallocation goes with first locking the heap, and retraining the unused block to the appropriate position.

🚀 Classes are more expensive to construct than structs because classes require a heap allocation. Because classes are allocated on the heap and have reference semantics, classes have some powerful characteristics like identity and indirect storage. But, if we don’t need those characteristics for abstraction, we’re going to better use a struct.

🚀 Structs aren’t prone to the unintended sharing of state like classes are.

🚀 Let’s say I have to cache the name of uiimages, so that compiler wouldn’t have to create the same images over and over. It would not be smart to cache the names with a String like this: let key = "\(color):\(oriantation):\(name)"

String isn’t particularly a strong type for this key. I’m using it to represent this configuration space, but I could just as easily put the name of my dog in that key. So, not a lot of safety there. Also, String can represent so many things because it actually stores the contents of its characters indirectly on the heap. So, that means every time we’re calling into this function, even if we have a cache hit, we’re incurring a heap allocation.

In Swift, we can represent this configuration space of color, orientation, and name just by using a struct. This is a much safer way to represent this configuration space than a String. And because structs are first class types in Swift, they can be used as the key in our dictionary.

struct Attributes {
    var color: Color // enum type
    var orientation: Orientation // enum type
    var name: Name //some custom enum type
}

Now the key can be below as structs are first class types in Swift, 
they can be used as the key in our dictionary:
let key = "let key = Attributes(color: color, orientation: orientation, name: name)"

Now, when we call the above function, if we have a cache hit, there’s no allocation overhead because constructing a struct like this attributes one, doesn’t require any heap allocation. It can be allocated on the stack. So, this is a lot safer and it’s going to be a lot faster.

Reference Counting

Swift keeps a count of the total number of references to any instance on the heap. And it keeps it on the instance itself. When you add a reference or remove a reference, that reference count is incremented or decremented. When that count hits zero, Swift knows no one is pointing to this instance on the heap anymore and it’s safe to deallocate that memory.

The key thing to keep in mind with reference counting is this is a really frequent operation and there’s actually more to it than just incrementing and decrementing an integer. First, there are a couple levels of indirection involved to just go and execute the increment and decrement. But, more importantly, just like with heap allocation, there is thread safety to take into consideration because references can be added or removed to any heap instance on multiple threads at the same time, we actually have to atomically increment and decrement the reference count. And because of the frequency of reference counting operations, this cost can add up.

There’s more to reference counting than incrementing, decrementing

  • Indirection
  • Thread safety overhead
  • Examples:
    • There is a class Point. Let’s assume I created an instance of Point which is point1, now point1 has gained an additional property, refCount. And we see that Swift has added a couple calls to retain and a couple calls to release. Retain is going to atomically increment our reference count and release is going to atomically decrement our reference count.
    • In this way, Swift will be able to keep track of how many references are alive to our point on the heap. And if we trace through this quickly, we can see that after constructing our point on the heap, it’s initialized with a reference count of one because we have one live reference to that point. As we go through our program and we assign point1 to point2, we now have two references and so Swift has added a call to atomically increment the reference count of our point instance. As we keep executing, once we’ve finished using point1, Swift has added a call to atomically decrement the reference count because point1 is no longer really a living reference as far as it’s concerned. Similarly, once we’re done using point2, Swift has added another atomic decrement of the reference count. At this point, no more references are making use of our point instance, so Swift knows it’s safe to lock the heap and return that block of memory to it.
    • There is a struct Point. Well, when we constructed our point struct, there was no heap allocation involved. When we copied, there was no heap allocation involved. There were no references involved in any of this. So, there’s no reference counting overhead for our point struct.

What about a more complicated struct, though?

Let’s assume I have a label struct that contains text which is of type String and font of type UIFont. String, as we heard earlier, actually stores the contents of its characters on the heap. So, that needs to be reference counted. And font is a class. And so that also needs to be reference counted. If we look at our memory representation, labels got two references. And when we make a copy of it, we’re actually adding two more references, another one to the text storage and another one to the font. The way Swift tracks these heap allocations is by adding calls to retain and release.

So, with the instances of label struct, we see the label is actually going to be incurring twice the reference counting overhead that a class would have.

In summary, because classes are allocated on the heap, Swift has to manage the lifetime of that heap allocation. It does so with reference counting. This is nontrivial because reference counting operations are relatively frequent and because of the atomicity of the reference counting.

If structs contain references, they’re going to be paying reference counting overhead as well.

🚀 In fact, structs are going to be paying reference counting overhead proportional to the number of references that they contain. So, if they have more than one reference, they’re going to retain more reference counting overhead than a class.

Examples:

struct Attachment {
    let fileURL: URL
    let uuid: String
    let mimeType: String

    init?(fileURL: URL, uuid: String, mimeType: String) {
        guard mimeType.isMimeType else { return nil }

        self.fileURL = fileURL
        self.uuid = uuid
        self.mimeType = mimeType
    }
}

Above, there is a lot of reference counting overhead and if we actually look at our memory representation of this struct, all 3 of our properties are incurring reference counting overhead when you pass them around. Because there are references to heap allocations underlying each of these structs.

Improve better:

enum MimeType: String {
    case png, jpeg, gif
}

struct Attachment {
    let fileURL: URL
    let uuid: UUID // It is now a struct
    let mimeType: MimeType // It is now an enum

    init?(fileURL: URL, uuid: UUID, mimeType: MimeType) {
        guard let mimeType = MimeType(rawValue: mimeType) else { return nil }

        self.fileURL = fileURL
        self.uuid = uuid
        self.mimeType = mimeType
    }
}

🚀 For UUID, which is really great because it stores those 128 bits inline directly in the struct. And so let’s use that. What this is going to do is, it’s going to eliminate any of the reference counting overhead we’re paying for that UUID field, the one that was a String. And we’ve got much more type safety because I can’t just put anything in here. I can only put a UUID. That’s fantastic. Let’s take a look at mimeType and let’s look at how I’ve implemented this isMimeType check. I’m actually only supporting a closed set of mimeTypes, JPG, PNG, and GIF.

🚀 Swift has a great abstraction mechanism for representing a fixed set of things. And that’s an enumeration. So, I’m going to take that switch statement, put it inside a failable initializer and map those mimeTypes to the appropriate case in my enum. So, now I’ve got more type safety with this mimeType enum and I’ve also got more performance because I don’t need to be storing these different cases indirectly on the heap. Swift actually has a really compact and convenient way of writing this exact code, which is using an enum that’s backed by a rawString value. And so this is effectively the exact same code except it’s even more powerful, has the same performance characteristics, but it’s way more convenient to write. So, if we looked at our attachment struct now, it’s way more type-safe. We’ve got a strongly typed UUID and mimeType field and we’re not paying nearly as much reference counting overhead because UUID and mimeType don’t need to be reference counted or heap allocated.

Method Dispatch

When you call a method at runtime, Swift needs to execute the correct implementation.

If it can determine the implementation to execute at compile time, that’s known as a static dispatch. And at runtime, we’re just going to be able to jump directly to the correct implementation. And this is really cool because the compiler actually going to be able to have visibility into which implementations are going to be executed. And so it’s going to be able to optimize this code pretty aggressively including things like inlining.

Static Dispatch

  • Jump directly to implementation at runtime.
  • Candidate for inlining and other optimizations.

Dynamic Dispatch

  • Look up implementation in the table at runtime.
  • Then jump to implementation.
  • Prevents inlining and other optimizations.

At dynamic dispatch, we’re not going to be able to determine a compile time directly which implementation to go to. And so at runtime, we’re actually going to look up the implementation and then jump to it. So, on its own, a dynamic dispatch is not that much more expensive than a static dispatch. There’s just one level of indirection. None of this thread synchronization overhead is like we had with reference counting and heap allocation.

🚀 But this dynamic dispatch blocks the visibility of the compiler. So while the compiler could do all these really cool optimizations for our static dispatches, at a dynamic dispatch, the compiler is not going to be able to reason through it.

What is inlining?

Let’s return to our familiar struct point.

struct Point {
    var x, y: Double
    func draw() {
        // Point.draw implementation
    }
}

func drawAPoint(_ param: Point) {
    param.draw()
}

let point = Point(x: 0, y: 0)
// When we call via drawAPoint function, compiler knows exactly which implementations are going to be executed 
// and so it's actually going to take our drawAPoint dispatch 
// and it's just going to replace that with the implementation of drawAPoint.
drawAPoint(point)

how compiler see above:
let point = Point(x: 0, y: 0)
// Point.draw implementation // Directly inside the draw() function

The drawAPoint function and the point.draw() method are both statically dispatched. What this means is that the compiler knows exactly which implementations are going to be executed and so it’s actually going to take our drawAPoint dispatch and it’s just going to replace that with the implementation of drawAPoint.

And then, it’s going to take our point.draw() method and, because that’s a static dispatch, it can replace that with the actual implementation of draw() function. So, when we go and execute this code at runtime, we’re going to be able to just construct our point, run the implementation, and we’re done. We didn’t need the overhead of those two static dispatches and the associated setting up of the call stack and tearing it down. So, this is really cool. And this gets to why static dispatches and how static dispatches are faster than dynamic dispatches.

🚀 A single static dispatch compared to a single dynamic dispatch, there isn’t that much of a difference, but in a whole chain of static dispatches, the compiler is going to have visibility through that whole chain. Whereas the chain of dynamic dispatches is going to be blocked at every single step from reasoning at a higher level without it. And so the compiler is going to be able to collapse a chain of static method dispatches just like into a single implementation with no call stack overhead. So, that’s really cool.

So, why do we have this dynamic dispatch thing at all?

It enables really powerful things like polymorphism. If we look at a traditional object-oriented program here with a drawable abstract superclass, I could define a point subclass and a line subclass that overrides draw with their own custom implementation. And then I have a program that can polymorphically create an array of drawables. Might contain lines. Might contain points. And it can call draw on each of them.

class Drawable { func draw() {} }

class Point: Drawable {
    var x, y: Double
    override func draw() {
        //...
    }
}

class Line: Drawable {
    var x1, y1, x2, y2: Double
    override func draw() {
        //...
    }
}

var drawables: [Drawable]
for d in drawables {
    d.draw()
}

Because drawable, point, and line are all classes, we can create an array of these things and they’re all the same size because we’re storing them by reference in the array. And then when we go through each of them, we’re going to call draw on them. So, we can understand why the compiler can’t determine at compile time which is the correct implementation to execute.

Because this d.draw, it could be a point, it could be a line. They are different code paths.

So, how does it determine which one to call? Well, the compiler adds another field to classes which is a pointer to the type information of that class and it’s stored in static memory. And so when we go and call draw, what the compiler actually generates on our behalf is a lookup through the type to something called the virtual method table on the type and static memory, which contains a pointer to the correct implementation to execute. And so if we change this d.draw to what the compiler is doing on our behalf, we see it’s actually looking up through the virtual method table to find the correct draw implementation to execute. And then it passes the actual instance as the implicit self-parameter.

So, what have we seen here?

Classes by default, dynamically dispatch their methods. This doesn’t make a big difference on its own, but when it comes to method chaining and other things, it can prevent optimizations like inlining and that can add up.

🚀 Not all classes, though, require dynamic dispatch. If you never intend for a class to be subclassed, you can mark it as final to convey to your fellow teammates and to your future self that that was your intention. The compiler will pick up on this and it’s going to statically dispatch those methods. Furthermore, if the compiler can reason and prove that you’re never going to be subclassing a class in your application, it’ll opportunistically turn those dynamic dispatches into static dispatches on your behalf.

Whenever you’re reading and writing Swift code, you should be looking at it and thinking,

  • Is this instance going to be allocated on the stack or the heap?
  • When I pass this instance around, how much reference counting overhead I’m going to incur?
  • When I call a method on this instance, is it going to be statically or dynamically dispatched?
    If we’re paying for dynamism we don’t need, it’s going to hurt our performance.

ref: https://developer.apple.com/videos/play/wwdc2016/416/

Advertisement

Mirroring and Reflection in Swift

Reflection: Is a form of meta programming that allows you to extract information from data structure in runtime

Swift’s version of reflection enables us to iterate over, and read the values of, all the stored properties that a type has — whether that’s a struct, a class, or any other type — enabling a sort of meta programming that can enable us to write code that actually interacts with the code itself.

https://www.swiftbysundell.com/articles/reflection-in-swift/

There is a Mirror struct, if you call on an object, it will give you an idea if it is class, struct, enum, tuple. But it does not work on all types for example closures. If it can not tell you what the type is, it will return nil.

There is a child property which has label and values. You can recursively go through the properties of a data structure and be able to sort of go down in a tree and really to figure out everything that is going on.

SwiftLint

SwiftLint is a tool to enforce Swift style and conventions. It is developed by Realm.

Installation:

In this page, we will install SwiftLint via Homebrew.

Open terminal and run

$ brew install swiftlint

Once installed, navigate to your project folder in terminal and create .swiftlint.yml file:

$ touch .swiftlint.yml

swiftlint.yml file is where we define how the linter configuration will be. In the configuration file, you can add, disable or update the linting rules.

You can see a sample of this file’s structure at here.

Rule inclusion in .swiftlint file:

  • disabled_rules: Disable rules from the default enabled set.
  • opt_in_rules: Enable rules not from the default set.
  • only_rules: Only the rules specified in this list will be enabled. Cannot be specified alongside disabled_rules or opt_in_rules.
  • analyzer_rules: This is an entirely separate list of rules that are only run by the analyze command. All analyzer rules are opt-in, so this is the only configurable rule list, there are no equivalents for disabled_rules only_rules.

Open the .swiftlint.yml file to edit:

$ open .swiftlint.yml

After you have configured the .swiftlint.yml file, save it and close.

Then navigate into your project – XCode Target > Build Phases > + icon > Create New Run Script Phase

Add below script:

if which swiftlint >/dev/null; then
  swiftlint
else
  echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi

Place your build script in top of Build Phases, Then build your project.

In my example project below, SwiftLint’s script is the second one.

There is a built-in feature in SwiftLint by which can correct some type of violations automatically.
Run below command in the root directory of your iOS project to autocorrect:

$ swiftlint autocorrect

When you run above command files on disk are overwritten with a corrected version. So to be aware that you have backups of these files before running, swiftlint autocorrect in case some datas can be lost.

Now that you have configured your linter in XCode, you may want to check some common Swift Style guides as well.

Run swift lint in custom iOS file

  • add your file location in -included tag:
included:
  - Source
  - Tests
  - ../

Here is my custom .swiftlint file.

Here you can find the source code and learn more about SwiftLint.

Closures in Swift

Closures are blocks of code that defines functionality in your code. Closures can capture and store references to any constants and variables from the context. Basically, swift handles all of the memory management of capturing. Yet, when building hierarchies of objects and closures you have to be very careful in considering which type that will act as the owner for each given instances. With a clear hierarchy, parent instances will be responsible in retaining their children in which, children instances will be “weak” references, and a lot of memory related problems will be avoided.

@Escaping: When the passing argument defined in outside of the closure and will be executed later this argument must be defined as @escaping. When the execution ends, the scope of the passed closure will exist in the memory till it gets executed. In such, asynchronous functions like waiting API response, or calling Grand Central Dispatch – like doing animation, will cause the @escaping argument to be stored in memory until the animation is done, or api delivers response. In those cases, we must explicitly  define self’s as “weak” properties to avoid memory issues.

@Non-Escaping: When the passing argument is already a in a function, closure executes the argument in the function itself and returns it to back the compiler. In those cases, once the execution ends, the passed closure goes out of scope and have no more existence in memory. (Default one)

Memory Management in Swift

Swift uses ARC memory management model.

Retain Cycles Problem: When two objects reference each other, or when capturing in closures may cause a retain cycles.

1. Referencing object increments object’s retain count: For example, lets say we have a Stationery.swift class and Notebook.swift class, and both these classes includes object of one another. Imagine that we have both instances of these classes, in that case retain cycles occur. For solution, we must break the retain cycle by making notebook’s instance to “weak”.

2. Closures: Another example could be about closures, just like how referencing an instance using a property increment its retain count, so does capturing instance in closure.
For example, if we are using a closure to observe a notebook instance whenever its being sold in stationery object, and using the same stationery object within that closure will again cause the retain cycles.

Publishing Libraries on Jitpack

Publishing Single Library on Jitpack

JitPack is a novel package repository for JVM and Android projects. It builds Git projects on demand and provides you with ready-to-use artifacts (jar, aar).
1. Create a new android project.
2.Add your library by File -> New -> Import a module.
3.Add the JitPack maven repository to the list of repositories:
repositories {
jcenter() maven { url "https://jitpack.io" }
}

Note: when using multiple repositories in build.gradle it is recommended to add JitPack at the end. Gradle will go through all repositories in order until it finds a dependency.
4. Then publish your new project to your GitHub account. Then, push your first version tag.
Note: You have to give permission from your account to jitpack.
single library example

Publishing Multiple Libraries on Jitpack

1. Create a new android project.
2. Add your libraries one by one by File -> New -> Import a module.
3. Add the JitPack maven repository to the list of repositories:
repositories {
jcenter() maven { url "https://jitpack.io" }
}

Note: when using multiple repositories in build.gradle it is recommended to add JitPack at the end. Gradle will go through all repositories in order until it finds a dependency.
4. Then publish your new project to your GitHub account. Then, push your first version tag.
Note: You have to give permission from your account to jitpack.
example for multiple libraries

Important Notes: 🚀
Lets assume you have module1, module2, module3 in your android project.
To share only one module, you can add:
implementation ‘com.github.yourproject:module1:module1’sTAG'
Or to share all your project directly:
implementation’com.github.yourproject:yourproject'sTAG'

If you are using apply plugins for such as androidanalyser, the developer who wants to use your project must embed it in her/his app. So be sure, that you embed as few plugins as possible.

Lastly, jitpack ignores files according to .gitignore file. So be sure you added necessary files to .gitignore.

baselineAligned

“Defines whether widgets contained in this layout are baseline-aligned or not.”

By setting android:baselineAligned="false" , app prevents the layout from aligning its children’s baselines, that means app doesn’t worry about where the baseline of other elements in the layout, which increases the UI performance.

Note: By default, baselineAligned is set to true.

TDD In Practise

When it comes to Testing In Swift, there are 3 keys,

1 – Design your code for clear testability.

Unified Input & Output: In the functional programming world there is this talk about pure function which simply means that same input will always produce the same output no matter where it is called. You don’t have to become Haskell programmers and change all your code according to pure functionality, yet, you may want to inspire by them to make your code easier to test.

Keep Our State Local: In the Apple Community a lot of people are using singleton patters simply because they are used to. We have seen Apple do it like, UIScreen, UIDevice, Bundle, etc. So we as IOS Devs, tend to use it by default even when we really don’t need it. As singletons can be nice for sharing some apis they can also lead you a dangerous paths such as undefined state. So before using it we must think that whether we really need it or not. In summary, my humble suggestion would be try to keep states in local so that we are gonna end up with less bugs and also easier to test the code.

Dependency Injection: When you are testing a state or functionality, all the needs to create output must be declared as you call the testing function, so try not to put variables in the class itself but try to create them while calling the function via putting those variables in initializers. So that, all the needs will always generate the same output with the same input no matter when or where.

2 – Remember that you are going to write a test against all your public api. So keep caution on access modifiers.

Always keep going with framework oriented programming, so that your codes will be easy to test and easy to reuse them.

3 – We all need mocks when it comes to testing, yet be very careful while using them. Remember your only purpose is writing tests to check your real code. Mocks come with a cost and more complexity. And you can end up instead of testing your api, testing too much of your implementation.

baselineAligned

“Defines whether widgets contained in this layout are baseline-aligned or not.”

By setting android:baselineAligned="false" , app prevents the layout from aligning its children’s baselines, that means app doesn’t worry about where the baseline of other elements in the layout, which increases the UI performance.

Note: By default, baselineAligned is set to true.

Proguard

ProGuard — shrinking, optimization, obfuscation, and preverification of Java bytecode.

Proguard optimizes the bytecode, removes unused codes, and obfuscates the classes, fields, methods with shorter names. Optimization operates with java bytecode. Since android runs on Dalvik bytecode which is converted from java bytecode, some optimizations usually doesn’t work well ( So you should be careful ).

The obfuscated code makes your APK difficult to reverse engineer, which is the main reason why most of the developers choose proguard.

Proguard can sometimes break your code up since it renames almost everything. Be sure to thoroughly test your app before release, especially after changing proguard config.

Prevent Obfuscation In Some Classes

Every app has some kind of data classes, models, or some important classes which they cannot be obfuscated. In such circumstances, we cannot let proguard to rename or remove any fields of those classes. It’s a safe bet to add a @Keep annotation on the whole class or methods, or a wildcard rule on proguard-rules.pro.

1 – @Keep Annotation:

Denotes that the annotated element should not be renamed when the code is minified at build time. This is typically used on methods and classes that are accessed only via reflection so a compiler may think that the code is unused.

@Keep
  public void foo() {
      ...
  }
 

Note that: This annotation is available only when using the Annotations Support Library.

2 – Using ProGuard, keep class fields with wildcard:

-keepclassmembers class com.my.package.** {
    public protected <fields>;
    private *** string*;
}

Tips & Tricks in Proguard