专栏首页小菜与老鸟Xcode编译疾如风系列-1.分析编译耗时

Xcode编译疾如风系列-1.分析编译耗时

Xcode 编译疾如风-1.如何排查编译耗时问题

开发 iOS 的小伙伴都知道,随着项目的不断庞大,Xcode 项目的编译时间也会越来越长。如果不加管控,会严重影响到我们的开发效率。

全量编译下,我去,30 分钟过去了,拉个屎还能抽根烟...

这不,小菜有些受不了了。开始研究 iOS 编译时长问题。

切入这个编译耗时问题,首先我们要分析编译的耗时在哪里,然后再针对性的进行优化。否则就像无头苍蝇一样乱飞乱撞,事倍功半。

关于这个系列文章有几点说明:1)这个系列会涉及到 Cocoapods,小菜的项目使用该工具进行组件管理。2)标题中提到的编译会涉及到全量编译和增量编译。

我们如何衡量构建时间呢?或者我们有哪些工具能够帮助我们分析构建时间?

ShowBuildOperationDuration

我们暂时先不看pod install或者pod update的组件安装耗时,单纯看 Xcode 的 build 耗时情况。如何得知 Xcode build 项目的时长?

一句命令行搞定:

defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES

终端执行完毕后,我们在使用 Xcode 编译时,便会在 Xcode 的状态条上显示编译时长

有读者朋友问了,如果我不用 Xcode 软件编译呢,比如我使用xcodebuild来编译,怎么获取到编译耗时呢?

time xcodebuild

很简单,shell 的基本操作,使用time便可获取执行的时间情况。

Build With Timing Summary

构建时长概要

同样的,如果我们用xcodebuild也可以获取构建时长概要:

xcodebuild -showBuildTimingSummary

XCLogParser

XCLogParser[1] 是一个命令行工具,用于分析 xcactivitylog 日志文件(注:xcactivitylog 是 Xcode 和 xcodebuild 在构建时保存的一种日志文件)

XCLogParser 可以提供项目中每个模块和文件的构建时间警告错误单元测试结果

我们在项目编译后,执行脚本:

xclogparser parse --project Kickstarter --reporter html

便可以在build/xclogparser/reports/时间戳目录下看到输出了大量的html,直接点击里面的index.html便可在浏览器中查看具体的编译信息。

我们可以将脚本执行在自动构建 CI 流程中,打完包后可以查看 CI 机器下生成的编译信息。CI 机器起一个静态服务即可。

Swift 代码编译耗时分析

如果项目中存在大量的 Swift 代码,且 Swift 的编译耗时成为了瓶颈,我们可以对 Swift 代码的编译耗时情况进行诊断。

类型检查警告

我们可以在Other Swift Flags配置检查警告项:

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

然后 Xcode 编译结束后,我们便可以在编译日志中看到函数/表达时编译耗时超过 100毫秒 的警告,点击这些警告便可以进入具体的代码位置,从而帮助我们优化代码。

编译器诊断选项

Swift 编译器性能[2]中,Apple 官方提到了几个诊断选项:

  • -driver-time-compilation
  • -Xfrontend -debug-time-function-bodies
  • -Xfrontend -debug-time-expression-type-checking
  • -Xfrontend -print-stats
  • -Xfrontend -print-clang-stats
  • -Xfrontend -print-stats -Xfrontend -print-inst-counts

我们重点关注-debug-time-function-bodies-debug-time-expression-type-checking

-debug-time-function-bodies可以统计打印出 Swift 文件中函数体编译耗时:

9.16ms  test.swift:15:6 func find<R>(_ range: R, value: R.Element) -> R where R : IteratorProtocol, R.Element : Eq
0.28ms  test.swift:27:6 func findIf<R>(_ range: R, predicate: (R.Element) -> Bool) -> R where R : IteratorProtocol
2.81ms  test.swift:40:6 func count<R>(_ range: R, value: R.Element) -> Int where R : IteratorProtocol, R.Element : Eq
0.64ms  test.swift:51:6 func countIf<R>(_ range: R, predicate: (R.Element) -> Bool) -> Int where R : IteratorProtocol
...

例如

xcodebuild -project 'Kickstarter.xcodeproj' \
-scheme 'Kickstarter-iOS' \
-configuration 'Debug' \
-sdk 'iphonesimulator' \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-compilation" |
    awk '/CompileSwift normal/,/Swift compilation/{print; getline; print; getline; print}' |
    grep -Eo "^CompileSwift.+\.swift|\d+\.\d+ seconds" |
    sed -e 'N;s/\(.*\)\n\(.*\)/\2 \1/' |
    sed -e "s|CompileSwift normal x86_64 $(pwd)/||" |
    sort -rn |
    head -3

25.6026 seconds Library/ViewModels/SettingsNewslettersCellViewModel.swift
24.4429 seconds Library/ViewModels/PledgeSummaryViewModel.swift
24.4312 seconds Library/ViewModels/PaymentMethodsViewModel.swift

-debug-time-expression-type-checking更细致,可以打印出表达式的编译耗时:

0.20ms  test.swift:17:16
1.82ms  test.swift:18:12
6.35ms  test.swift:19:8
0.11ms  test.swift:22:5
0.02ms  test.swift:24:10
0.02ms  test.swift:30:16
...

例如

xcodebuild -project 'Kickstarter.xcodeproj' \
-scheme 'Kickstarter-iOS' \
-configuration 'Debug' \
-sdk 'iphonesimulator' \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-expression-type-checking \
    -Xfrontend -debug-time-function-bodies" |
  grep -o "^\d*.\d*ms\t[^$]*$" |
  awk '!visited[$0]++' |
  sed -e "s|$(pwd)/||" |
  sort -rn |
  head -5

16226.04ms Library/Styles/UpdateDraftStyles.swift:31:3
10551.24ms Kickstarter-iOS/Views/RewardCardContainerView.swift:171:16 instance method configureBaseGradientView()
10547.41ms Kickstarter-iOS/Views/RewardCardContainerView.swift:172:7
8639.30ms Kickstarter-iOS/Views/Controllers/AddNewCardViewController.swift:396:67
8233.27ms KsApi/models/templates/ProjectTemplates.swift:94:5

后续小菜还会输出 Swift 编译耗时优化的文章,敬请期待。

BuildTimeAnalyzer

BuildTimeAnalyzer[3] 是一款开源工具,使用很简单。其本质还是利用 Swift 编译器的诊断选项将耗时部分输出出来。

更多阅读

  • Xcode Build Time Optimization 1[4]
  • Xcode Build Time Optimization 2[5]

参考资料

[1]

XCLogParser: https://github.com/spotify/XCLogParser

[2]

Swift 编译器性能: https://github.com/apple/swift/blob/main/docs/CompilerPerformance.md#diagnostic-options

[3]

BuildTimeAnalyzer: https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode

[4]

Xcode Build Time Optimization 1: https://www.onswiftwings.com/posts/build-time-optimization-part1/

[5]

Xcode Build Time Optimization 2: https://www.onswiftwings.com/posts/build-time-optimization-part2/

本文分享自微信公众号 - 小菜与老鸟(xiaocai_laoniao),作者:ChildhoodAndy

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Xcode编译疾如风系列-2.并行编译

    Xcode 编译是一个需要 CPU 运算以及大量 IO 操作的过程。我们将编译任务分配给多个 CPU 来提高单位时间内的编译速度。

    ChildhoodAndy
  • Xcode编译疾如风-3.浅谈 dwarf 和 dSYM

    小菜:本文是《Xcode编译疾如风-4.BuildSettings》的其中的Debug Information Format 配置项的背景知识前置科普文。

    ChildhoodAndy
  • iOS 微信编译速度优化分享

    前言 岁月真是个养猪场,这几年,人胖了,微信代码也翻了。记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟。如今用公司配的 17 年款 27-in...

    微信终端开发团队
  • www6663388com请拨18687679362环球国际iOS 微信编译速度优化分享

    岁月真是个养猪场,这几年,人胖了,微信代码也翻了。记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟。如今用公司配的 17 年款 27-inch i...

    用户7106032
  • 如何解决 iOS 环境搭建与 APP 打包速度问题

    随着 Flutter 等跨端框架的出现,业务开发同学经常需要在 Android/IOS 上跨端进行业务开发,问题定位等。新的不熟悉的环境的搭建总会遇到各种各样的...

    编程怪才-凌雨画
  • 微信团队分享:极致优化,iOS版微信编译速度3倍提升的实践总结

    本文来自微信开发团队WeMobileDev公众号的原创技术分享,原题“iOS 微信编译速度优化分享”,即时通讯网收录时排版及部分文字有修订和优化。

    JackJiang
  • iOS开发中的CPU架构问题

    首先,之所以提到CPU架构的问题,其实是因为我们iOS开发使用的静态库与动态库与之紧密相连。每一个开发的App都要运行在模拟器或者真机上,在编译的时候,Xcod...

    梧雨北辰
  • XcodeXcode 9 的全新功能您会喜欢的工具。内建 Interface BuilderXcode IDE

    在 Apple 平台上打造精彩超凡的 app 所需要的一切,Xcode 9 都已为您准备好。即使在编辑最大的文件时,它也能保持超乎想象的高速流畅。它比以往更了解...

    iOSDevLog
  • 移动端性能测试必备工具PerfDog性能狗

    在项目研发支持过程中,经历如上障碍和痛苦。我们决定做一个完全独立、简单易用,与APP版本、系统版本、系统平台无任何关系的性能平台。

    WeTest质量开放平台团队

扫码关注云+社区

领取腾讯云代金券