今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

iOS Compilation Time Optimization

Background#

Currently, the App project is not large, but the compilation time after cleaning takes more than 200 seconds, which doesn't seem reasonable. Therefore, I started investigating it.

Compilation time optimization is usually divided into three parts:

  • Optimization of Xcode compilation settings
  • Optimization of code or function compilation time
  • Optimization of third-party library compilation time

Here, I will investigate each of these parts one by one.

Implementation#

Optimization of Xcode Compilation Settings#

I am using Xcode 13.4, and according to what I found online, there is no need to set the New Build System in Xcode or the Debug Information Format in Build Settings anymore because they are already set to reasonable defaults. As for the Optimization Level setting, although it can improve compilation speed, it is not friendly for debugging, so I won't make any changes to it. Therefore, I didn't handle this optimization.

Optimization of Code or Function Compilation Time#

This mainly applies to Swift. First, display the methods that take a long time to compile by adding the following settings to Other Swift Flags in Build Settings. This means that functions that take more than 200ms to compile or type checks that take more than 300ms to compile will display warnings. The value of 200ms is set by myself and can be adjusted based on the actual situation of the project:


-Xfrontend -warn-long-function-bodies=200
-Xfrontend -warn-long-expression-type-checking=200

Here are a few examples of modifications I made:

Example of Code to be Optimized 1:


let count: Int = Int((self?.listParamItem.pageSize ?? 0) * ((self?.needRefreshPageNum ?? 0) - 1))
let endIndex = count + Int(self?.listParamItem.pageSize ?? 0) - 1
if (self?.dataList.count ?? 0) > endIndex {

In the above code, the mixing of optional chaining and the nil-coalescing operator (??) followed by type conversion makes the compilation time really long. Although the code is correct, these methods take more than 500ms to compile. To optimize them, I modified the code as follows, reducing the compilation time to less than 100ms, which is a 5-fold reduction:


if let self = self {
  let count: Int = self.listParamItem.pageSize * (self.needRefreshPageNum - 1)
  let endIndex: Int = count + self.listParamItem.pageSize - 1
  if self.dataList.count > endIndex {
}

Example of Code to be Optimized 2:


let dic = [
    "aaa": xxx ?? yyy, 
    "bbb": ["ccc": "xxx", "eee": 5],
    "ddd": 5
]

In the above code, there doesn't seem to be any problem, but the compilation time exceeds 200ms, possibly due to type inference. After modifying the code as follows, the compilation time no longer exceeds 200ms:


var dic1: [String: Any] = [:]
dic1["ccc"] = "xxx"
dic1["eee"] = 5

var dic2: [String: Any] = [:]
dic2["aaa"] = xxx
dic2["bbb"] = dic1
dic2["ddd"] = 5

Example of Code to be Optimized 3:


if type == .aaa ||
type == .bbb ||
type == .ccc ||
type == .ddd ||
type == .eee ||
type == .xxx {
  doSomething()
} else {
    doAnotherThing()
}

The above code is not only inelegant but also takes a long time to compile. I modified it to use Switch case as follows:


switch type {
case .aaa,
.bbb,
.ccc,
.ddd,
.eee,
.xxx: 
  doSomething()
default:
    doAnotherThing()
}

Example of Code to be Optimized 4:


let fontAdd: CGFloat = 14.0

protocolBtn.snp.makeConstraints { make in
    make.left.equalTo(agreeLabel.snp.right).offset(1)
    make.centerY.equalTo(checkBtn.snp.centerY)
    make.width.equalTo(kTransitionW(150 + fontAdd * 10))
}

In the above code, there is a mixture of floating-point and integer arithmetic operations, which requires Swift to infer whether a floating-point or integer result is needed. After modifying it as follows, the compilation timeout warning disappears:


let fontAdd: CGFloat = 14.0
let width: CGFloat = 150.0 + fontAdd * 10.0

protocolBtn.snp.makeConstraints { make in
    make.left.equalTo(agreeLabel.snp.right).offset(1.0)
    make.centerY.equalTo(checkBtn.snp.centerY)
    make.width.equalTo(kTransitionW(width))
}

Finally, there is a category of methods that are particularly long, which also triggers compilation timeout warnings. To solve this, I split them into multiple sub-methods.

Optimization of Third-Party Library Compilation Time#

The third-party libraries integrated into the project are all integrated using CocoaPods. Therefore, every time a clean build is performed, the third-party libraries will be recompiled. To display the compilation time in Xcode, open the terminal, copy the following command, and run it, then restart Xcode.


$ defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES

Then clean and rebuild the project. You can see detailed compilation times as follows:

Compilation Time

Here, ZLPhotoBrowser is a third-party library installed through CocoaPods, and you can see that the compilation of its source files took 30 seconds... This page provides detailed compilation times for each library and the project. For third-party libraries with long compilation times, consider importing them using Carthage instead. You can refer to Using Carthage. Carthage downloads and compiles the third-party libraries to generate xcframeworks, which can then be imported into the project. Therefore, they won't be recompiled every time a clean build is performed, saving compilation time.

Summary#

The three parts of compilation time optimization can be summarized as follows:

  • Optimization of Xcode compilation settings - No need to set in the latest version of Xcode.
  • Optimization of code or function compilation time - Pay attention to optimizing type inference, complex calculations, and the use of operators. However, not all code that exceeds the compilation timeout needs to be optimized. You need to find a balance between compilation optimization and code simplicity, elegance, and Swift features.
  • Optimization of third-party library compilation time - This optimization is recommended. For third-party libraries that won't be modified, switch to using Carthage for importing them. This can reduce the recompilation time.

It is recommended to analyze where the project's compilation time is being spent before optimization, whether it is due to long compilation times of third-party libraries or source files of the project. This will help determine whether to focus on step 2 or step 3.

References#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.