После внедрения Swift в проект значительно увеличилось время сборки, что стало для нас существенным препятствием. В своём докладе я расскажу о том, как мы решили эту проблему, сократив время компиляции более чем в два раза.
4. Tune build settings
Build Active Architecture Only
Enable Objective-C Modules
Debug data format - DWARF (no dsym file)
Enable Whole module optimization
6. Tune build settings
Whole module optimization
Main target build time: 76.614 s
Not suitable for debugging
Can’t compile unit test target - segfault or weird errors with
Swift-ObjC bridging
7. Reduce .swift files count
Pre-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
8. Reduce .swift files count
Merge 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
15. Avoid long expressions
Optimize 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
16. 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 }
}()
17. Use map() and flatMap() with care
Optimize 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
18. 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 s
func 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
19. Optimize slow compiling functions
Build time: 92.5s (94.3s before)
No great effect in our case
Before:
• 8659 functions > 1ms
• max time 2871.3мс
• median time 1.8ms
After:
• 2227 functions > 1ms
• max time 124.3мс
• median time 3.4ms
20. Fix warnings
Swift Compiler Warnings: 64 total
Warning type Count
Build time
after fix
Parameters of ... have different optionality 3 92.5s
User-defined 2 92.5s
Cannot find protocol definition 3 90.481s
Overriding 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.654s
Immutable value ... was never used 1 93.152s
Pointer is missing nullability specifier 9 80.667s
Build time: 80.667s (92.5s before)
21. Fix warnings
But, with 1 warning <Some code> will never been executed, build
time is 84.919s
Fix all Swift Compiler Warnings to speed up build time!
22. 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
23. 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
24. Injection for Xcode plugin:
+ dynamically inserts new Swift / Objective-C code into a
running app
+ support «tunable parameters»
Source: https://github.com/johnno1962/injectionforxcode
25. 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.
26. 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/
27. Further legacy code refactoring, remove Swift -
Objective C dependencies.
Plans