Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift Performance Tips for Busy iOS Developers

Swift Performance Tips for Busy iOS Developers

Want to learn quick tips that will improve your apps performance and load time? In this talk, we’ll explore the latest Swift techniques we can apply to create faster iOS apps. Topics discussed will include:
* The three dimensions of performance:
* Allocation (stack vs heap)
* Reference counting (less vs more)
* Method dispatch (static vs dynamic)
* Is Swift faster than Objective-C?
* Are structs faster than classes?
* Is inheritance faster than protocol extensions?
* Grand Central Dispatch patterns
* Whole module optimization and more

Brad Broulik

March 12, 2017
Tweet

More Decks by Brad Broulik

Other Decks in Technology

Transcript

  1. 2 Performance Timeline (Past) Jan Feb Mar Apr May Jun

    Jul Aug Sep Oct Nov Dec Management demands 2X performance boost by Q4 Developers play video games for 8 months Developers install the latest CPUs Management thanks developers for doubling performance Moore’s Law
  2. 3 Performance Timeline (Present) Jan Feb Mar Apr May Jun

    Management demands 2X performance boost by Q2 Developers optimize their code utilizing every technique possible (GCD, caching, code optimizations, etc) Management thanks developers for doubling performance Moore's Law is Dead. Now What?
  3. 4 Dimensions of Performance Slower Dimension Faster Heap Allocation Stack

    More Reference Counting Less Dynamic Method Dispatch Static Is this instance allocated on the stack or heap?
  4. 7 Dimensions of Performance Slower Dimension Faster Heap Allocation Stack

    More Reference Counting Less Dynamic Method Dispatch Static When I pass this instance around how much reference counting am I going to incur?
  5. 10 Dimensions of Performance Slower Dimension Faster Heap Allocation Stack

    More Reference Counting Less Dynamic Method Dispatch Static When I call a method on this instance is it statically or dynamically dispatched?
  6. 14 (lower is better) Seconds 0.0 4.3 8.5 12.8 17.0

    Convert 1,000 UUIDs to Strings 15.89 0.12 0.07 forEach map for-in Avoid for-in Gist: Swift forEach vs map vs for-in performance test map for-in forEach
  7. 16 •isEmpty is more efficient because it only evaluates the

    first element; count > 0 evaluates all elements when the collection does not conform to RandomAccessCollection. •Want to enforce this practice in your code base? SwiftLint has a static analysis rule for it. Prefer isEmpty (lower is better) Milliseconds 0.0 0.5 1.0 1.5 2.0 1 million items in Set 1.41 0.26 isEmpty count Gist: Swift isEmpty vs count performance test isEmpty) count)
  8. 18 Slower Dimension Faster Heap Allocation Stack More Reference Counting

    Less Dynamic Method Dispatch Static •All methods become statically dispatched when a class is final •final may be set per method as needed •private methods are implicitly final •final expresses developer intent more clearly Prefer final classes Final class) Regular class)
  9. 20 Prefer enums over Strings Slower Dimension Faster Heap Allocation

    Stack More Reference Counting Less Dynamic Method Dispatch Static •enums are allocated on the stack; String characters are allocated on the heap. •enums do not incur a reference count; Strings incur a reference count. •enums are type safe; Strings are not. •Interested in automatically converting your localizable strings, asset names, or storyboard names to enums? SwiftGen has accomplished this task nicely on my projects. struct B) struct A)
  10. 22 Prefer structs over classes •structs are allocated on the

    stack; classes are allocated on the heap. •structs do not incur reference counting (when their properties are all value types) •structs implicitly use static dispatch •structs prevent unintended sharing •A structs member wise initializer is a nice convenience Slower Dimension Faster Heap Allocation Stack More Reference Counting Less Dynamic Method Dispatch Static struct) class)
  11. 24 Bonus: Which is faster? (lower is better) Seconds 15.0

    15.5 16.0 16.5 17.0 50,000 copies 16.57 15.71 class struct Gist: Swift struct vs class (with many strings) struct) class)
  12. 2 25 Struct with many strings Person String String String

    Person String String String Class 2 Class 2 Class Stack Stack Heap a: y: Optimizing Swift Performance 1 2 3 4 5 6
  13. 26 Class with many strings Person String String String Stack

    Heap 2 (reference) (reference) b: z: 1 2
  14. 27 Prefer Structs over Classes (until you reach their point

    of diminishing returns) •structs do not incur reference counting (when their properties are all value types) •Caution: A struct property will incur reference counting and heap allocation if the property has a reference type for underlying storage (string characters are stored on the heap). Therefore, structs have a point of diminishing returns in regards to reference counting that multiplies proportionally by their number of properties which are reference types. A struct containing more than 1 reference type begins to incur a higher reference count than a comparative class.
  15. Inheritance Extensions 29 Inheritance uses dynamic dispatch Uses static dispatch

    Dynamic dispatch definitions Static dispatch definitions Which is faster? Protocol extension method dispatch flow chart
  16. 30 Prefer extensions over inheritance •Protocols allow us to define

    methods with either static or dynamic dispatch; inheritance uses dynamic dispatch. •Methods defined within protocol extensions use static dispatch and methods defined within protocol types are dynamically dispatched •Protocol extensions are not bound to single inheritance •Protocol extensions may be applied to both classes or structs •Unfortunately, protocols can not inherit stored properties but classes can Slower Dimension Faster Heap Allocation Stack More Reference Counting Less Dynamic Method Dispatch Static
  17. 34 Prefer Generics over Protocol Types Understanding Swift Performance •Generic

    methods will be optimized via generic specialization during Whole Module Optimization •Generic specialization creates a type-specific version of method that enables static dispatch. A type-specific method will be created per type in use. •If generic implementation is a struct you also get allocation on the stack and no reference counting (when struct contains no references) •And you still get all the benefits of polymorphism Slower Dimension Faster Heap Allocation Stack More Reference Counting Less Dynamic Method Dispatch Static
  18. 35 Parse JSON on a Background Thread Concurrent Programming with

    GCD in Swift 3 Main Thread Dispatch Queue User Interface Data Transform Data
  19. 37 Enable Whole Module Optimization Optimizing Swift Performance Builds take

    longer but the generated binary will run faster because the compiler makes more aggressive generic specialization and will automatically enable static dispatch when no overrides exist
  20. 38 Dimensions of Performance Slower Dimension Faster Heap Allocation Stack

    More Reference Counting Less Dynamic Method Dispatch Static Ask yourself these questions when reading or writing Swift code: 1. Is this instance allocated on the stack or heap? 2. When I pass this instance around how much reference counting am I going to incur? 3. When I call a method on this instance is it statically or dynamically dispatched? Understanding Swift Performance
  21. 39 Performance Timeline (Future) Jan Feb Mar Apr May Jun

    Management demands performance boost by Q2 Swift developer enables Whole Module Optimization Management thanks developers for improving performance Swift developers play video games for 2 months