paint-brush
Swift Runtime Optimization: Boosting Project Speed with Simple Techniquesby@bugorbn
258 reads

Swift Runtime Optimization: Boosting Project Speed with Simple Techniques

by Boris BugorNovember 23rd, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Swift runtime optimization offers a plethora of techniques to accelerate project speed dynamically. Learn how to limit dynamic dispatch in classes and methods by using the final prefix or making methods private. Dive into the intriguing world of inline optimization, copying code calculations directly to the calling place. Uncover the efficiency of ContiguousArray for non-ObjC data types, ensuring optimal performance. This guide provides essential strategies to unleash Swift's full potential and optimize your project's runtime speed effectively.
featured image - Swift Runtime Optimization: Boosting Project Speed with Simple Techniques
Boris Bugor HackerNoon profile picture


There are a sufficient number of ways to increase the speed of a project at runtime. Let’s look at the most popular of them:


Limiting dynamic dispatch in classes

Under the hood, any new class will use dynamic (table) dispatch. This is a price for the ability to be inherited, but if the class is not inherited, you can save a significant amount of resources. The final prefix will turn your class into a statically dispatched object.


Before optimization:

// parent class with dynamic table dispatch
class A {}
// child class with dynamic table dispatch
class B: A {}


After optimization:

// child class with static dispatch
final class B: A {}


Examples of speed boost


  • Parent class A:




  • Subclass B:



  • Final class B:




Limitation of dynamic dispatch at methods

A similar approach is used for class methods, but there are several options available:


  • If your method is not used outside the class, feel free to use private.
  • If you are going to protect your method from the possibility of override, use final.


Both prefixes will turn your method into a statically dispatched method but with different scopes.


Before optimization:


// method with dynamic table dispatch
func method() {}


After optimization:


// private method with static dispatch
private func method() {}

// method with static dispatch cannot be override
final func method() {}


Examples of speed boost


  • Nonfinal method:



  • Final or private method:




Inline optimization

One of the most curious and rarely used types of optimization. Inline optimization allows you to copy code calculations directly to the place where a given function is called without calling a method marked with inline, which reduces the number of method calls and works faster than static dispatch.


Among the pitfalls, in order to force the compiler to call a method with inline optimization — it is necessary to mark the method not only @inlineable but also @inline(__always)


It is important not to mark methods that spend a large amount of resources on copying with these prefixes; there is a risk of filling the cache of all processor levels with calculations of these methods and, as a result, getting slower instead of speeding up.


Before optimization:


// test method with multiple calling
func testMethod() {}


After optimization:


// inlinable test method with multiple calling
@inlinable
@inline(__always)
func testMethod() {}


Examples of speed boost


  • regular calling:


  • inlined calling:




Optimization in protocols

Under the hood, protocols in Swift can be implemented for reference types and value types. Due to the fact that the memory management mechanism for reference types and value types are different, you can significantly speed up the compiler by limiting protocol adoption to reference types only.


Before optimization:


// can be adopted to reference types and value types,
// but value types adoption is unnecessarily
protocol Implementable {}


After optimization:


// can be adopted to reference types only
protocol Implementable: AnyObject {}


Examples of speed boost


  • creation array elements with a protocol for reference and value type



  • creation array elements with protocol for reference type only



Arrays

Array was originally intended not only as a collection type for Swift, allowing elements of the same type to be stored sequentially, but also as a collection type providing compatibility with NSArray.


In case the array is intended to store non-ObjC data types, it is recommended to use ContagiousArray for more efficient operation of the array.


Before optimization:


// creation array with NSArray interop
let array: Array<Int> = [1, 2, 3]


After optimization:


// creation array without NSArray interop
let fastArray: ContiguousArray<Int> = [1, 2, 3]


Examples of speed boost:


  • creation of Array:



  • creation of ContagiousArray:



Also published here.

Don’t hesitate to contact me on Twitter if you have any questions. Also, you can always buy me a coffee.