"Ускорение сборки большого проекта на objective-c + swift"...
TRANSCRIPT
![Page 1: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/1.jpg)
Speed up build time of big project on
Objective-C + SwiftIvan Bondar
Lead iOS developer in Avito
![Page 2: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/2.jpg)
Build time: 194.803s
75% of build time - Compile Swift files phase
Swift: - *.swift files: 626 - LOC: 27264
Project structureObjective-C:
- *.m files: 729 - LOC: 45947 - 217 imports in bridging-header
![Page 3: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/3.jpg)
What to do• Tune build settings • Reduce .swift files count • Reduce extensions count • Optimize slow compiling functions • Fix warnings • Apply ccache compiler cache utility
![Page 4: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/4.jpg)
Tune build settingsBuild Active Architecture Only
Enable Objective-C Modules
Debug data format - DWARF (no dsym file)
Enable Whole module optimization
![Page 5: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/5.jpg)
Tune build settingsDebug data format - DWARF (no dsym file)
Build time: 191.623s (194.803s before)
![Page 6: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/6.jpg)
Tune build settingsWhole module optimization
Main target build time: 76.614 s Not suitable for debuggingCan’t compile unit test target - segfault or weird errors with Swift-ObjC bridging
![Page 7: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/7.jpg)
Reduce .swift files countPre-build action - merge all Swift code to one FAT Swift file
Not suitable for big projects: - can’t compile with segmentation fault 11 - eliminates «private» modifier - can’t use breakpoints in the original source
![Page 8: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/8.jpg)
Reduce .swift files countMerge different classes/protocols in big .swift files
Not suitable for VIPER in general Decided to apply only in certain cases, e.g. put Input and Output protocol declarations in one file
![Page 9: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/9.jpg)
Reduce extensions count
0
10
20
30
40
100 1000 2000 3000 5000 10000methods extensions
* by Dmitry Bespalov https://tech.zalando.com/blog/speeding-up-xcode-builds/
![Page 10: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/10.jpg)
Reduce extensions count
class FilterViewController: UIViewController { }
// MARK: UITableViewDelegate extension FilterViewController: UITableViewDelegate { }
// MARK: UITableViewDataSource extension FilterViewController: UITableViewDataSource { }
class FilterViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // MARK: UITableViewDelegate // MARK: UITableViewDataSource }
Changes in code style applied
Before: After:
Extensions count reduced by 400 Build time: 94.3s (191.623s before)
![Page 11: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/11.jpg)
Optimize slow compiling functionsProfile compile time per function, filter by time > 1 ms.
xcodebuild ARCHS=arm64 ONLY_ACTIVE_ARCH=NO -workspace App.xcworkspace -scheme App clean build OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" | grep [1-9].[0-9]ms | sort -nr > culprits.txt
![Page 12: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/12.jpg)
Optimize slow compiling functions
What to fix: • Functions with longest compile time • Functions with big number of occurrences
2871.3ms /Users/iyubondar/Projects/avito-ios/Models/Domain/Advertisement/Advertisement/Base/AdvertisementImage.swift:2:5 init(url100x75: String?, url140x105: String?, url240x180: String?, url432x324: String?, url640x480: String?, url1280x960: String?)932.7ms /Users/iyubondar/Projects/avito-ios/Core/Extensions/UIKitExtensions/UICollectionView/UICollectionView+ChangeAnimations.swift:57:17
final class func changeSet<T : Hashable>(oldArray oldArray: [T], newArray: [T]) -> CollectionViewChangeSet253.0ms /Users/iyubondar/Projects/avito-ios/Core/Extensions/UIKitExtensions/UICollectionView/UICollectionView+ChangeAnimations.swift:90:17
final class func changeSet<T>(oldArray oldArray: [T], newArray: [T], identityHashFunction: (T) -> Int, identityCheckFunction: (T, T) -> Bool, equalityCheckFunction: (T, T) -> Bool = default) -> CollectionViewChangeSet136.0ms /Users/iyubondar/Projects/avito-ios/Presentation/Views/Advertisement/AdvertisementView/AdvertisementPresenter.swift:9:26 @objc public override func setModel(model: AnyObject!)91.4ms /Users/iyubondar/Projects/avito-ios/Presentation/Views/Controls/PullToRefresh/ScrollViewRefresher.swift:251:18 private func handleRefreshingProgressChanged(progress: RefreshingProgress)84.0ms /Users/iyubondar/Projects/avito-ios/VIPER/SelectCategoryParameters/Validation/SelectFromToValidator.swift:52:25 private final class func findValidRowIndex(valuesToSelect: [SelectCategoryParameterViewModel.Data], inCompareToValues compareValues: [SelectCategoryParameterViewModel.Data]?, selectedRowIndex: Int, iterationOrder: IterationOrder) -> Int82.5ms /Users/iyubondar/Projects/avito-ios/Presentation/Views/Profile/ProfileViewPresenter.swift:30:10 @objc func notificationsSubtitle() -> String81.8ms <invalid loc> init?(rawValue: String)currentDate: NSDate, calendar: NSCalendar, todayDateFormatter: NSDateFormatter, yesterdayDateFormatter: NSDateFormatter, weekdayDateFormatter: NSDateFormatter, dayDateFormatter: NSDateFormatter, yearDayDateFormatter: NSDateFormatter, fullDateFormatter: NSDateFormatter) -> String
8659 occurrences found.
![Page 13: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/13.jpg)
Use lazy only when it's necessary
1204 occurrences found Compile time: 1.0 … 11.8 ms
Optimize slow compiling functions
private lazy var footerLabel: UILabel = { let footerLabel = UILabel() footerLabel.textColor = SpecColors.mainText footerLabel.font = SpecFonts.regular(14) footerLabel.autoresizingMask = .FlexibleWidth footerLabel.lineBreakMode = .ByWordWrapping footerLabel.numberOfLines = 0 footerLabel.textAlignment = .Center footerLabel.shadowColor = UIColor.whiteColor() footerLabel.shadowOffset = CGSize(width: 0, height: -1) return footerLabel }()
![Page 14: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/14.jpg)
Avoid long expressions
Compile time: 2871.3ms
Optimize slow compiling functions
self.thumbnailUrl = url240x180 ?? url140x105 ?? url100x75 ?? nil self.fullImageUrl = url640x480 ?? url432x324 ?? url1280x960 ?? url240x180 ?? url140x105 ?? url100x75 ?? nil
self.thumbnailUrl = url240x180 ?? url140x105 ?? url100x75 self.fullImageUrl = url640x480 ?? url432x324 ?? url1280x960 ?? url240x180 ?? url140x105 ?? url100x75
Compile time: 916 ms
![Page 15: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/15.jpg)
Avoid long expressionsOptimize slow compiling functions
var fullImageUrl: String? { if let url640x480 = url640x480 { return url640x480 } if let url432x324 = url432x324 { return url432x324 } … if let url100x75 = url100x75 { return url100x75 } return nil }
Compile time: <1 ms
![Page 16: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/16.jpg)
Use map() and flatMap() with care
1177 occurrences found Compile time: 2.5 … 21.8 ms
Optimize slow compiling functions
private lazy var tabControllers: [UIViewController] = { var controllers = [UIViewController?](count: Tab.tabsCount, repeatedValue: nil) controllers[Tab.Search.rawValue] = self.categoriesNavigationController() controllers[Tab.Favorites.rawValue] = self.favoritesNavigationController() controllers[Tab.Publish.rawValue] = self.publishNavigationController() controllers[Tab.Messenger.rawValue] = self.channelsRootViewController() controllers[Tab.Profile.rawValue] = self.profileNavigationController() return controllers.flatMap { $0 } }()
![Page 17: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/17.jpg)
Use map() and flatMap() with careOptimize slow compiling functions
Original example: http://irace.me/swift-profiling
return [CustomType()] + array.map(CustomType.init) + [CustomType()]
Compile time: 3158.2 ms and many occurrences
![Page 18: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/18.jpg)
Give «type hints» to the compiler when necessary
* example by IMPATHIC http://blog.impathic.com/post/99647568844/debugging-slow-swift-compile-times
Optimize slow compiling functions
func hangCompiler() { ["A": [ ["B": [ 1, 2, 3, 4, 5 ]], ["C": [ ]], ["D": [ ["A": [ 1 ]] ]] ]] }
Build time: 54.249 sfunc hangCompiler() { ["A": [ ["B": [ 1, 2, 3, 4, 5 ]] as [String: [Int]], ["C": [ ]] as [String: [Int]], ["D": [ ["A": [ 1 ]] as [String: [Int]] ]] ]] }
Build time: 1.293 s
![Page 19: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/19.jpg)
Optimize slow compiling functions
Build time: 92.5s (94.3s before)
No great effect in our caseBefore:
• 8659 functions > 1ms • max time 2871.3мс • median time 1.8ms
After: • 2227 functions > 1ms • max time 124.3мс • median time 3.4ms
![Page 20: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/20.jpg)
Fix warningsSwift Compiler Warnings: 64 total
Warning type Count Build time after fix
Parameters of ... have different optionality 3 92.5sUser-defined 2 92.5sCannot find protocol definition 3 90.481sOverriding instance method parameter with implicitly unwraped optional type 27 warnings 27 90.195s
Deprecation 18 93.487s<Some code> will never been executed 1 93.654sImmutable value ... was never used 1 93.152sPointer is missing nullability specifier 9 80.667s
Build time: 80.667s (92.5s before)
![Page 21: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/21.jpg)
Fix warningsBut, with 1 warning <Some code> will never been executed, build time is 84.919s
Fix all Swift Compiler Warnings to speed up build time!
![Page 22: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/22.jpg)
Apply ccache compiler cache utility
Build time: 68.403s (80.667s before)
ccache limitations: - no support for Clang modules - no support for precompiled headers - no Swift support
ccache applied to project
![Page 23: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/23.jpg)
What was done
Before: 194.803 s After: 68.403 s
There is no silver bullet :(• Tune build settings - 3s • Reduce .swift files count - 0s • Reduce extensions count - 97s • Optimize slow compiling functions - 2s • Fix warnings - 12s • Apply ccache compiler cache utility -12s
![Page 24: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/24.jpg)
Injection for Xcode plugin:+ dynamically inserts new Swift / Objective-C code into a
running app + support «tunable parameters»
Source: https://github.com/johnno1962/injectionforxcode
![Page 25: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/25.jpg)
Injection for Xcode plugin:
Source: https://github.com/johnno1962/injectionforxcode
Swift limitations. It’s not possible to: - Make changes to Structs. - Change functions or classes that are marked as final. - Change global functions or variables that are not
constrained into a class.
![Page 26: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/26.jpg)
Split app into frameworks with no cyclic dependencies between classes of different frameworks *
Plans
* idea and image: http://bits.citrusbyte.com/improving-swift-compile-time/
![Page 27: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/27.jpg)
Further legacy code refactoring, remove Swift - Objective C dependencies.
Plans
![Page 28: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/28.jpg)
Links
• http://bits.citrusbyte.com/improving-swift-compile-time/ • http://stackoverflow.com/questions/25537614/why-is-swift-compile-time-so-slow
General:
• http://blog.impathic.com/post/99647568844/debugging-slow-swift-compile-times • http://irace.me/swift-profiling
Debug slow compile time:
• https://tech.zalando.com/blog/speeding-up-xcode-builds/ • https://gist.github.com/lucholaf/e37f4d26e406250a156a
Speed up builds:
• https://developer.apple.com/videos/play/wwdc2015/409/ • http://useyourloaf.com/blog/swift-whole-module-optimization/ • http://useyourloaf.com/blog/modules-and-precompiled-headers/ • https://labs.spotify.com/2013/11/04/shaving-off-time-from-the-ios-edit-build-test-cycle/ • http://tomj.io/2015/08/29/speed-up-your-swift-test-builds-by-70-percent.html • https://pewpewthespells.com/blog/managing_xcode.html#dep-imp
Build settings:
• https://pspdfkit.com/blog/2015/ccache-for-fun-and-profit/ • https://ccache.samba.org/manual.html#_configuration
ccache utility:
![Page 29: "Ускорение сборки большого проекта на Objective-C + Swift" Иван Бондарь (Avito)](https://reader034.vdocuments.net/reader034/viewer/2022052117/587cdd851a28abff0b8b60c1/html5/thumbnails/29.jpg)
Questions?