Slide 1

Slide 1 text

Making build times great again SCHIBSTED iOS JAM 2018 Xavier Jurado iOS Lead @ Schibsted Spain

Slide 2

Slide 2 text

Making build times great bearable again SCHIBSTED iOS JAM 2018 Xavier Jurado iOS Lead @ Schibsted Spain

Slide 3

Slide 3 text

Agenda → Motivation → Benchmarks → Recap 3/51

Slide 4

Slide 4 text

Motivation 4/51

Slide 5

Slide 5 text

5/51

Slide 6

Slide 6 text

6/51

Slide 7

Slide 7 text

Optimize build times → Lots of tips & tricks available → Some outdated information → Some myths → Lack of up to date analytical data 7/51

Slide 8

Slide 8 text

Benchmarks 8/51

Slide 9

Slide 9 text

Methodology → Real life project → Ruby script + fastlane → Xcode 9.3.1, debug configuration, simulator → 5 iterations cleaning derived data → 5 incremental iterations → https://github.schibsted.io/xavier-jurado/ios- common--xcode-benchmark 9/51

Slide 10

Slide 10 text

Benchmarks 1. Baseline 2. Function & expression optimization 3. Number of files/classes 4. New build system 5. dSYM generation 6. CPU 7. WMO 10/51

Slide 11

Slide 11 text

Baseline 11/51

Slide 12

Slide 12 text

28 % 72 % Swift Objective-C Baseline → Coches.net project → 4 years old → ~700 source files → 44k loc → Cocoapods (~20 dependencies) → Forced default settings 12/51

Slide 13

Slide 13 text

Baseline Non incremental → Average: 159.76s → Stdev: 3.9s Incremental → Average: 9.5s → Stdev: 0,05s 13/51

Slide 14

Slide 14 text

Function & expression optimization 14/51

Slide 15

Slide 15 text

Motivation → Type checking: historical problem → Still an issue? Edge cases? 15/51

Slide 16

Slide 16 text

Execution Swift compiler diagnostic optionss1: OTHER_SWIFT_FLAGS=-driver-time-compilation -Xfrontend -debug-time-function-bodies -Xfrontend -debug-time-compilation … plus awk, grep, sed magick1 k1 https://koke.me/2017/03/24/diving-into-swift-compiler-performance/ s1 https://github.com/apple/swift/blob/master/docs/ CompilerPerformance.md#diagnostic-options 16/51

Slide 17

Slide 17 text

Results 11.5645 ( 42.5%) {compile: PopTip.o <= PopTip.swift} 10.6891 ( 8.9%) {compile: CameraPresenter.o <= CameraPresenter.swift} 10.6874 ( 8.9%) {compile: CameraUI.o <= CameraUI.swift} 10.6855 ( 8.9%) {compile: CameraViewController.o <= CameraViewController.swift} 10.6844 ( 8.9%) {compile: CheckCameraAccess.o <= CheckCameraAccess.swift} 10.6829 ( 8.9%) {compile: CheckPhotoAccess.o <= CheckPhotoAccess.swift} 10.6814 ( 8.9%) {compile: CollectionSelectorPresenter.o <= CollectionSelectorPresenter.swift} 10.6795 ( 8.9%) {compile: CollectionSelectorTableViewCell.o <= CollectionSelectorTableViewCell.swift} 10.6781 ( 8.9%) {compile: CollectionSelectorTableViewDataSource.o <= CollectionSelectorTableViewDataSource.swift} 10.1187 ( 11.5%) {compile: BadgeManager.o <= BadgeManager.swift} 10.1169 ( 11.5%) {compile: DeviceTokenManager.o <= DeviceTokenManager.swift} 10.1133 ( 11.5%) {compile: KnockerError.o <= KnockerError.swift} 10.1118 ( 11.4%) {compile: KnockerNotification.o <= KnockerNotification.swift} 10.1104 ( 11.4%) {compile: Knocker.o <= Knocker.swift} 10.1102 ( 11.4%) {compile: KnockerTrackingModule.o <= KnockerTrackingModule.swift} ... > 400 files took more than 1 second to compile 17/51

Slide 18

Slide 18 text

Results 11.5645 ( 42.5%) {compile: PopTip.o <= PopTip.swift} 10.6891 ( 8.9%) {compile: CameraPresenter.o <= CameraPresenter.swift} 10.6874 ( 8.9%) {compile: CameraUI.o <= CameraUI.swift} 10.6855 ( 8.9%) {compile: CameraViewController.o <= CameraViewController.swift} 10.6844 ( 8.9%) {compile: CheckCameraAccess.o <= CheckCameraAccess.swift} 10.6829 ( 8.9%) {compile: CheckPhotoAccess.o <= CheckPhotoAccess.swift} 10.6814 ( 8.9%) {compile: CollectionSelectorPresenter.o <= CollectionSelectorPresenter.swift} 10.6795 ( 8.9%) {compile: CollectionSelectorTableViewCell.o <= CollectionSelectorTableViewCell.swift} 10.6781 ( 8.9%) {compile: CollectionSelectorTableViewDataSource.o <= CollectionSelectorTableViewDataSource.swift} 10.1187 ( 11.5%) {compile: BadgeManager.o <= BadgeManager.swift} 10.1169 ( 11.5%) {compile: DeviceTokenManager.o <= DeviceTokenManager.swift} 10.1133 ( 11.5%) {compile: KnockerError.o <= KnockerError.swift} 10.1118 ( 11.4%) {compile: KnockerNotification.o <= KnockerNotification.swift} 10.1104 ( 11.4%) {compile: Knocker.o <= Knocker.swift} 10.1102 ( 11.4%) {compile: KnockerTrackingModule.o <= KnockerTrackingModule.swift} ... 18/51

Slide 19

Slide 19 text

import Foundation extension Data { func toHexString() -> String { return self.reduce("", {$0 + String(format: "%02.2hhx", $1)}) } } @objc public class DeviceTokenManager: NSObject { private static let key:String = "knocker.device.token" static let defaults = UserDefaults.standard public static func store(_ deviceToken: Data) { defaults.set(deviceToken.toHexString(), forKey: key) defaults.synchronize() } public static func retrieve() -> String? { return defaults.object(forKey: key) as? String } public static func clear() { defaults.removeObject(forKey: key) } } 19/51

Slide 20

Slide 20 text

Conclusions → Quick wins. → Risk: write for the compiler instead of the developer. 20/51

Slide 21

Slide 21 text

Number of files vs number of classes 21/51

Slide 22

Slide 22 text

Motivation → As projects get bigger, Xcode gets slower → By how much? → Who's to blame? Code complexity? File count? 22/51

Slide 23

Slide 23 text

Execution → Add 100, 300 and 900 randomly generated classes to the project → Check incremental & absolute build times → Compare with 900 randomly generated classes inside a single file 23/51

Slide 24

Slide 24 text

// Autogenerated import Foundation class BenchmarkSwiftClass_0 { let dep: BenchmarkSwiftClass_15 private let iteration = 0 init(dep: BenchmarkSwiftClass_15) { self.dep = dep } } 24/51

Slide 25

Slide 25 text

Results 25/51

Slide 26

Slide 26 text

Conclusions → Huge cost on incremental builds. → Shall we take it into account when creating protocols, extensions, etc? 26/51

Slide 27

Slide 27 text

New build system 27/51

Slide 28

Slide 28 text

Motivation → Provides higher reliabilitya1 → Catches many project configuration problems. → Improves overall build-system performance. a1 https://developer.apple.com/library/content/documentation/DeveloperTools/ Conceptual/WhatsNewXcode/xcode9/xcode9.html 28/51

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Execution UseNewBuildSystem=YES 30/51

Slide 31

Slide 31 text

Results ! 31/51

Slide 32

Slide 32 text

Conclusions → Performance wise it's a net loss. → May make Xcode more stable 32/51

Slide 33

Slide 33 text

dSYM generation 33/51

Slide 34

Slide 34 text

Motivation → "Popular culture" → DWARF vs DWARF + dSYM? → dSYM files are only needed for external analysis (ie: release builds) 34/51

Slide 35

Slide 35 text

Execution → DEBUG_INFORMATION_FORMAT: "dwarf" 35/51

Slide 36

Slide 36 text

Results meh. 36/51

Slide 37

Slide 37 text

Conclusions → No tangible effect 37/51

Slide 38

Slide 38 text

CPU 38/51

Slide 39

Slide 39 text

Motivation → Real life experience between MBP 13 (2 cores) & MBP 15 (4 cores) is abysmal. → What about faster processors? More cores? 39/51

Slide 40

Slide 40 text

Execution Computer CPU #Cores Freq Max Freq MacbookPro Mid 2015 i7-4980HQ 4 2,8 Ghz 4 Ghz Hackintosh Late 2017 i7-7700k 4 4.2 GHz 4.5 Ghz MacbookPro Mid 2017 i7-7820HQ 4 2.9 Ghz 3.9 Ghz MacbookPro Mid 2014 i7-4578U 2 3 Ghz 3.5 Ghz https://technical.city/en/cpu/Core-i7-7700K-vs-Core-i7-4980HQ 40/51

Slide 41

Slide 41 text

Results 41/51

Slide 42

Slide 42 text

Conclusions → #cores matters a lot. → If you don't have a 4 core machine, ask for one . → CPU matters, but newer != better. → Apple hardware is terrible has room for improvement. 42/51

Slide 43

Slide 43 text

WMO Whole Module Optimizations 43/51

Slide 44

Slide 44 text

Motivation → Introduced in Xcode 8 to produce faster Release binaries by applying optimizations that can only be performed to a whole module s2. → WMO: Compilation Mode + Optimization Level → It significantly changes the way the compiler runs. s2 https://swift.org/blog/whole-module-optimizations/ 44/51

Slide 45

Slide 45 text

Execution → Pre Xcode 9.3: SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule" OTHER_SWIFT_FLAGS="-Onone" → Xcode 9.3: has its own setting SWIFT_COMPILATION_MODE 45/51

Slide 46

Slide 46 text

As expected…? 46/51

Slide 47

Slide 47 text

! 47/51

Slide 48

Slide 48 text

Conclusions → Greatly reduces absolute build times. → Can increase incremental build times. → Better for projects with lots of files. → Give it a try! 48/51

Slide 49

Slide 49 text

Recap 49/51

Slide 50

Slide 50 text

Worth your time 1. CPU 2. WMO (for big projects) 3. Function/expression optimization 4. Be aware of the implications of having lots of small files Not worth it → New build system (but may be worth it for Xcode stability) → dSYM 50/51

Slide 51

Slide 51 text

Thanks! 51/51