有效提升Swift编译速度

Swift编译太慢, 提升编译速度, Swift Long Compile Time

Posted by yuan on December 1, 2016

前言


Swift作为一个新兴的语言,有着苹果Dad(dy???)的支持与良好的社区环境。于是乎大家都开始慢慢尝试在项目中使用Swift。在我们的项目中也开始慢慢使用,但是过慢的编译时间真的是killing us。 完整的编译一次可能需要15到20分钟,完全不能忍。

先说结果,在我的电脑MacPro上,项目编译时间直接从以前20m41s缩短了10m16s。 而这10分钟仅仅只是从的代码层面带来的效果

定位问题


国外友人Robert已经为我们写好了一个Swift编译时间定位的工具【请戳这里】。 方便好用,可以立竿见影的找到问题代码。

ps:以前也用XCTOOL但是xocde8已经不再支持build了,非常可惜。

代码优化


指定类型与拒绝泛型

原码:

var model : UILabel?
var cat : String?
var name : String?
var number : Int?
//build tiem : 8740.3ms
func sendData() {
    let parameter = ["model" : model?.text ?? "",
                     "cat" : cat ?? "",
                     "name" : name ?? "",
                     "number" : number ?? 0,
                     "dog" : "dog"]
    print("send request with parameter:\(parameter)")
}

这段代码所需要的编译时间是8740.3ms,就这么一个字典定义,我们竟然浪费了8秒!

指定类型之后我们再来看看

//build time: 3235.4ms
let parameter : [String : AnyObject] = ["model" : model?.text ?? "",
		                       "cat" : cat ?? "",
		                       "name" : name ?? "",
		                       "number" : number ?? 0,
		                       "dog" : "dog"]

这次所需要的编译时间就已经缩短为了3235.4ms,通过手动指定类型,我们缩短了5s以上的时间让费

再紧接,我们继续观察,这个parameter的字典其实可以全部为String,根本用不到AnyObject。

指定特定的类型

//build time: 200.3ms
let parameter : [String : String] = ["model" : model?.text ?? "",
					"cat" : cat ?? "",
					"name" : name ?? "",
					"number" : "\(number ?? 0)",
					"dog" : "dog"]

这次所需要的编译时间就已经缩短为了200.3ms,把AnyObject ->String ,我们缩短了2s以上的时间让费!!

所以如果你可以一种类型搞定,请千万别写AnyObject!!!

通过指定正确的类型我们从8740ms的编译时间缩短到了200ms!!!!

运算时nil保护抽离

//build time : 9804ms
func calculateSize(view : UIView?) -> CGSize{
    return CGSize(width: 10 + (view?.bounds.width ?? 0) + (view?.bounds.height ?? 0) + 22, height: 20)
}

//build time : 172ms
func calculateSize(view : UIView?) -> CGSize{
    let width = view?.bounds.width ?? 0
    let height = view?.bounds.height ?? 0
    return CGSize(width: 10 + width + height + 22, height: 20)
}

这段代码编译了9804ms,只是因为我们在运算的时候一并加入nil的保护。如果我们拆离nil保护,编译时间缩短了98.3%

  • 使用三目运算(Bool ? a : b)时也非常耗时,但还没有到非常严重的程度,一个三目可能需要额外的 100ms200ms编译时间
  • 当你在字典中使用nil保护时,也可能造成极长的编译时间,有时候甚至会长达20s.但不是每次都出现。我理项目时就通过BuildTimeAnalyzer发现了很多这样的问题。比如:["model" : model?.text ?? ""]. 在通过把他们强制转化成想要的类型String后得到解决:["model" : (model?.text ?? "")as String]。 暂时还不知道为什么。 猜测是因为 model?.text 的text属性是一个可选型, compiler花费了很长的时间来确定到底是Optional(String)还是String.但又不是每次都出现,非常奇怪。

少用++=运算符

//build time 1400.6ms
func arrPlusOperatos() {
    let arr1 = [1,2,3]
    let arr2 = [3,4,5]
    result += arr1 + arr2 + [10]
}
//build time 8.6ms
func arrPlusOperatos() {
    let arr1 = [1,2,3]
    let arr2 = [3,4,5]
    result.appendContentsOf(arr1)
    result.appendContentsOf(arr2)
    result.appendContentsOf([10])
}

尽量少的使用++=号来合并参数, 在项目中有一些array的合并编译时间高达5000ms.

对于String也是一样的,String 使用\(value)来合并值,或API给的append.

总结


上面的几个问题是在整理项目(Swift2.3)中,特别明显影响编译速度的点:

  • 指定类型、拒绝泛型
  • 运算时nil保护抽离、少用三目运算
  • 少用+、+=运算符 缩短了我们接近50%的Swift编译时间。

具体大家可以用BuildTimeAnalyzer来查看项目哪些func存在严重的编译过长问题。

更多:

  1. regarding-swift-build-time-optimizations
  2. swift-compiler-performance-tips-and-tricks
  3. why-is-swift-compile-time-so-slow

后续


框架上的提高编译性能:

  • 模块化代码,使用私有Cocoapods repository. 让不同模块以Framework或则.a文件的形式在项目里使用。如此每次编译的时候就只需要编译自己模块下的代码。其他模块的代码将会被编译后缓存,不需要重复编译。

其他一些优化包括:

  • Find Implicit Dependencies Off [1]
  • Build Active Architecture Only Yes On Debug [2]
  • Precompile Prefix Header set to YES [3]
  • defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks 4 [4]
  • RAM Disk [5]没有尝试过,有机会可以试一下

最近文章

重定义你的XCode Console

前言自从Xcode8之后, 再也不能像以前一样愉快的使用插件了。之前赖以生存Console插件XcodeColors 也随之失效。我们再一次回到了原生只有黑白信息的Console上面。虽然可以用一些Formatting的Log来弥补,但依旧不是很直观。每一次当测试机同事的手机器上有什么BUG需...…

iOS继续阅读
更早文章

Wallpaper - Swift/OC Closures/Block

A Wallpaper I have created as Swift and Objective-C block reminder Reference:[1] Swift closures and functions[2] How Do I Declare A Block in Objec...…

Swift继续阅读