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

Making Xcode build times great again

Making Xcode build times great again

[email protected]

May 24, 2018
Tweet

Other Decks in Programming

Transcript

  1. Making build times great bearable again SCHIBSTED iOS JAM 2018

    Xavier Jurado iOS Lead @ Schibsted Spain
  2. Optimize build times → Lots of tips & tricks available

    → Some outdated information → Some myths → Lack of up to date analytical data 7/51
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. Motivation → As projects get bigger, Xcode gets slower →

    By how much? → Who's to blame? Code complexity? File count? 22/51
  11. 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
  12. // Autogenerated import Foundation class BenchmarkSwiftClass_0 { let dep: BenchmarkSwiftClass_15

    private let iteration = 0 init(dep: BenchmarkSwiftClass_15) { self.dep = dep } } 24/51
  13. Conclusions → Huge cost on incremental builds. → Shall we

    take it into account when creating protocols, extensions, etc? 26/51
  14. 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
  15. Motivation → "Popular culture" → DWARF vs DWARF + dSYM?

    → dSYM files are only needed for external analysis (ie: release builds) 34/51
  16. Motivation → Real life experience between MBP 13 (2 cores)

    & MBP 15 (4 cores) is abysmal. → What about faster processors? More cores? 39/51
  17. 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
  18. 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
  19. 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
  20. Conclusions → Greatly reduces absolute build times. → Can increase

    incremental build times. → Better for projects with lots of files. → Give it a try! 48/51
  21. 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