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

Improving App Launch Time

Improving App Launch Time

Presented by Prashant Rane

Swift India

January 25, 2020
Tweet

More Decks by Swift India

Other Decks in Programming

Transcript

  1. Exception Type: EXC_CRASH (SIGKILL) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note:

    EXC_CORPSE_NOTIFY Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d Termination Description: SPRINGBOARD, process-launch watchdog transgression: com.go-jek.xxxxx.develop exhausted real (wall clock) time allowance of 20.00 seconds | ProcessVisibility: Unknown | ProcessState: Running | WatchdogEvent: process-launch | WatchdogVisibility: Foreground | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 27.590 (user 27.590, system 0.000), 69% CPU", | "Elapsed application CPU time (seconds): 0.128, 0% CPU" | ) Triggered by Thread: 0
  2. Agenda • What affects launch timing (Measure and Improve) •

    Why did we faced the issue • How we zeroed on the approach to fix (Pre-Main only) • How we fixed this problem • Key takeaway from the journey
  3. What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS

    • dylib load time, rebase/binding, objc setup, Static Initializers
  4. What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS

    • dylib load time, rebase/binding, objc setup, Static Initializers across code • Post-Main • Measure using Instruments
  5. What affects launch timing • Pre-Main • Measure using DYLD_PRINT_STATISTICS

    • dylib load time, rebase/binding, objc setup, Static Initializers across code • Post-Main • Measure using Instruments • Time between call to main() and ability to handle touch
  6. What affects launch timing • iOS version, because DYLD version

    ships with OS • Battery Level (Power Saver mode)
  7. What affects launch timing • iOS version, because DYLD version

    ships with OS • Battery Level (Power Saver mode) • Hardware Capacity
  8. What affects launch timing • iOS version, because DYLD version

    ships with OS • Battery Level (Power Saver mode) • Hardware Capacity • Binary Size
  9. App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection

    / Core Data/ Model Layer Initialization • User Location? • Analytics? •
  10. App(Will)DidFinishedLaunching • Singleton initializations (Facebook, Firebase etc?) • DB connection

    / Core Data/ Model Layer Initialization • User Location? • Analytics? • Setup Key Window • … many more
  11. ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC) • Prepare

    Datasource/ViewModel/VIPER stack • Time require to layout any Complex View
  12. ViewRendering (RootVC) • Setup Navigation Container (e.g. TabBarVC) • Prepare

    Datasource/ViewModel/VIPER stack • Time require to setup View (complex layout) • Any other work done in ViewDid(Will)Load (Auth, StateRestoration etc)
  13. Post-Main Use os_signpost for taking accurate measures of a method

    which is contributing to launch time. Ex: ViewDidLoad of RootViewController
  14. Post-Main Use os_signpost for taking accurate measures of a method

    which is contributing to launch time. Ex: ViewDidLoad of RootViewController
  15. Dyld: The WorkHorse • Rebase • Adjust pointers inside image

    • Slid = actual_address - preferred_address
  16. DYLD_PRINT_STATISTICS Total pre-main time: 249.96 milliseconds (100.0%) dylib loading time:

    191.67 milliseconds (76.6%) rebase/binding time: 15.74 milliseconds (6.2%) ObjC setup time: 9.54 milliseconds (3.8%) initializer time: 33.00 milliseconds (13.2%) slowest intializers : libSystem.B.dylib : 2.49 milliseconds (0.9%) libBacktraceRecording.dylib : 5.08 milliseconds (2.0%) libMainThreadChecker.dylib : 14.41 milliseconds (5.7%) libViewDebuggerSupport.dylib : 7.90 milliseconds (3.1%)
  17. Code Metric • 12000+ Types (Class, Struct, Enum…) • >

    8000 Swift files, ~1000 ObjC files (Pods)
  18. Code Metric • 12000+ Types (Class, Struct, Enum…) • >

    8000 Swift files, ~1000 ObjC files (Pods) • >80 Dynamic Libraries (and growing)
  19. target 'Vendor' do dynamic_frameworks internal_dynamic_frameworks special_dynamic_frameworks inherit! :search_paths target 'Framework1'

    do inherit! :search_paths end target 'Framework2' do inherit! :search_paths end end target 'DemoApp' do static_frameworks host_only_static_libs dynamic_frameworks internal_dynamic_frameworks special_dynamic_frameworks end
  20. Development Silos • Team Structure: independent development • Abstract Target

    in Podfile to make it magical • Complex Inter Target dependancies
  21. Development Silos • Team Structure: independent development • Abstract Target

    in Podfile to make it magical • Complex Inter Target dependancies • Messed up Build Settings per product
  22. Finding a solution • IDEAL: • Reduce internal (by merging)

    and external (building inhouse) framework count
  23. Finding a solution • IDEAL: • Reduce internal (by merging)

    and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product
  24. Finding a solution • IDEAL: • Reduce internal (by merging)

    and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product • Create create an Umbrella Framework from all Dependancies (Dynamic Framework) •
  25. Finding a solution • IDEAL: • Reduce internal (by merging)

    and external (building inhouse) framework count • Make dependancies as Static Frameworks and link that with Product • Create create an Umbrella Framework from all Dependancies (Dynamic Framework) • All Deps: Static library?
  26. Fixing the problem • Find out dependancies in different targets

    • Create a Lite app version to compare app launch time
  27. Fixing the problem • Find out dependancies in different targets

    • Create a Lite app version to compare app launch time • Prepare a checklist during the process of migration
  28. Fixing the problem • Find out dependancies in different targets

    • Create a Lite app version to compare app launch time • Prepare a checklist during the process of migration • Automate whatever we can from Checklist
  29. Problems Faced • Splitting Framework into Static Libs and Resource

    Bundle (DEMO) • Custom script to copy all resource bundles to app
  30. Problems Faced • CocoaPods + New Build System issues Build

    system information error: Multiple commands produce '/Users/Ben/Library/ Developer/Xcode/DerivedData/myApp- awbgyiqiegnrtdayecemnuyfqnbc/Build/Products/Debug- iphonesimulator/myApp.app': 1) Target 'myApp' has create directory command with output '/Users/Ben/Library/Developer/Xcode/DerivedData/myApp- awbgyiqiegnrtdayecemnuyfqnbc/Build/Products/Debug- iphonesimulator/myApp.app' 2) That command depends on command in Target 'myApp': script phase “[CP] Copy Pods Resources”
  31. Problems Faced • Implicit dependancies not working; so as the

    Parallel builds • That needs updating all target’s linker flags
  32. Problems Faced • As Implicit Dependancies were off, we need

    to add all imports explicitly • A script to automate inserting some must have imports
  33. Problems Faced • IntentDefinition was not getting compiled correctly (autogen

    classes were not generated) • Copied the generated files manually
  34. Problems Faced • R.Swift was not working with Static Libs

    + Resource Bundle model • Run the r.swift script from resource bundle and add generated files to library • Later replaced R.swift with inhouse code
  35. Problems Faced • Change in Framework name affected DLS components

    in IB files (ModuleName for a class) • XIB files using component from another framework, and now the name of framework is changed
  36. Problems Faced • Archive failed couple of times: • One

    header for static library was in copied in sources • ResourceBundle executable was not code signed (Demo the plist)
  37. Problems Faced • A few Crashes due to wrong scheme

    selected for archive (for some products)
  38. Open Issues • Required to Drop Support for Armv7 ld:

    section __bundle (address=0x06748000, size=735061037) would make the output executable exceed available address range for architecture armv7 • A bit longer build times even after switching to New Build System • Pods still add copy resources step in static lib • Incremental builds are not building Delta changes only
  39. Takeaways • Performance is equally important as a feature •

    Should be a Continuous Process (Don’t wait till blast)
  40. Takeaways • Performance is equally important as a feature •

    Should be a Continuous Process (Don’t wait till blast) • Measure what matters (part of CI?) • Using XCUITest
  41. Takeaways • Performance is equally important as a feature •

    Should be a Continuous Process (Don’t wait till blast) • Measure what matters (part of CI?) • Using XCUITest • Automate things which developer won’t be touching • Deployment target, swift version, optimization level etc
  42. Thank You • https://developer.apple.com/videos/play/wwdc2016/406/ • https://useyourloaf.com/blog/slow-app-startup-times/ • https://github.com/leavez/cocoapods-static-swift-framework • https://blog.automatic.com/how-we-cut-our-ios-apps-

    launch-time-in-half-with-this-one-cool-trick-7aca2011e2ea • https://medium.com/@kolyuchiy/a-record-breaking-story-of- boosting-the-launch-time-of-mail-rus-email-app-for- ios-59a266dd288a