Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Reduce Build Times and Get Home Eariler

Reduce Build Times and Get Home Eariler

Swift is a robust language, but at the same time, it means that many things are done at build time. Swift is now the standard for iOS development, but as a result, we suffer longer builds and it deprives us of our precious time. Time is more valuable than anything.

In this session, I will show you how to improve Swift’s build times and gain productivity in three parts:

- Measurement
- Improvement
- Avoid building all together!

Yusei Nishiyama

April 20, 2017
Tweet

More Decks by Yusei Nishiyama

Other Decks in Technology

Transcript

  1. • iOS, Android, Web • 63 countries • 17 languages

    • 63M+ MAU (Japan) • 35M+ MAU (Outside Japan) https://info.cookpad.com/en https://cookpad.com/en
  2. Let’s improve the registration screen… • Implement • Tweak constraints

    • Test with older iOS versions • Fix tests • Wait for CI
  3. ?

  4. What I will “not” talk about • Annotate all types

    explicitly • Write as much as code in a single file • Get your Mac Pro
  5. cookpad/dokumi • Open source • Advanced Lint tool • Comments

    issues on a pull request • Runs on CI server
  6. require 'benchmark'
 
 def measure(*args, &block) start_time = Time.now tms

    = Benchmark.measure(args, &block) @data << ExecutionLog.new(args, tms, start_time) end Benchmark
  7. Build Process • Dependency Manager (CocoaPods, Carthage) • Compile source

    files • Function A • Function B • … • Runs Scripts (Linter, Formatter)
  8. Is that data easy to manipulate? • Integrate data collection

    into our build process • Flexible format which doesn’t not restrict how we use it
  9. giginet/xcprofiler • A CLI tool to time body functions •

    Easy to integrate into build process • Flexible output (stdout, JSON, Custom)
  10. require 'xcprofiler' profiler = Xcprofiler::Profiler.by_product_name('MyApp') profiler.reporters = [ Xcprofiler::StandardOutputReporter.new(limit: 20,

    order: :time), Xcprofiler::JSONReporter.new(output_path: 'result.json'), Xcprofiler::BlockReporter.new do |executions| do_something(executions) end, ] profiler.report! Custom Reporter
  11. xactivitylog • You can access build logs under DerivedData •

    xactivitylog is just gzip. You can check the content as a plain text just by unzipping it
  12. InfluxDB • Only purpose-built platform for any time-based data •

    Written in Go • Built-in HTTP API • SQL-like query language • Answer queries in real-time • Rich client libraries
  13. Grafana • Rich metrics dashboard and editor • Fast rendering

    • Support InfluxDB as a data source • Query editor
  14. Test with Real Data $ cloc
 
 github.com/AlDanial/cloc v 1.72

    T=3.40 s (153.3 files/s, 16646.6 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Swift 497 8944 3122 43831 JSON 18 0 0 522 Objective C 3 37 5 166 C/C++ Header 4 19 6 27 ------------------------------------------------------------------------------- SUM: 522 9000 3133 44546 -------------------------------------------------------------------------------
  15. Test with Real Data • 40,000 lines of Swift code

    • CocoaPods • 38 direct dependencies and 60 libraries • Linter in build phases • CI archives the app and distributes it for internal testing
  16. Machine specs • CI • Mac mini (Late 2014) •

    3 GHz Intel Core i7 • Memory 16GB • 256GB SSD • Local • MBP (Retina, 15-inch, Late 2013) • 2.3 GHz Intel Core i7 • Memory 16 GB • 256GB SSD
  17. post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_OPTIMIZATION_LEVEL']

    = '-Owholemodule' unless config.name == 'Release' other_swift_flags = config.build_settings['OTHER_SWIFT_FLAGS'] || ['$(inherited)'] other_swift_flags << '-Onone' config.build_settings['OTHER_SWIFT_FLAGS'] = other_swift_flags end end end end
  18. post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['SWIFT_OPTIMIZATION_LEVEL']

    = '-Owholemodule' unless config.name == 'Release' other_swift_flags = config.build_settings['OTHER_SWIFT_FLAGS'] || ['$(inherited)'] other_swift_flags << '-Onone' other_swift_flags << '-Xfrontend' other_swift_flags << '-debug-time-function-bodies' config.build_settings['OTHER_SWIFT_FLAGS'] = other_swift_flags end end end end
  19. F

  20. Carthage + github "ReactiveX/RxSwift" ~> 3.4 + github "RxSwiftCommunity/RxRealm" +

    github "realm/realm-cocoa" ~> 2.4 + github "danielgindi/Charts" ~> 3.0.2 - pod 'RxSwift', '3.3.1' - pod 'RxCocoa', '3.3.1' - pod 'RxRealm', '0.5.2' - pod 'RealmSwift', '2.4.4' Cartfile Podfile
  21. Partial Lint lint() { while read filename; do if [[

    "${filename##*.}" == "swift" ]]; then ${SWIFT_LINT} lint --path "${filename}" --quiet fi done } FORK_POINT=`git merge-base --fork-point origin/develop` git diff $FORK_POINT --name-only | lint
  22. Real World Prototyping class ViewController: UITableViewController { let myView =

    MyView() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .myColor view.addSubview(myView) } }
  23. How to use it in Playground import UIKit @testable import

    MyPlayground let color = UIColor.myGreen
  24. dylib Loading Time Total pre-main time: 4.2 seconds (100.0%) dylib

    loading time: 4.0 seconds (94.4%) rebase/binding time: 93.32 milliseconds (2.1%) ObjC setup time: 44.64 milliseconds (1.0%) initializer time: 98.30 milliseconds (2.3%)
  25. Recap • You can get detailed build time information •

    Visualization helps you to identify the bottleneck • You can improve build time in many ways (WMO and Carthage are highly effective) • Modularization makes your prototyping easier