[Xcode]一种低成本的link耗时优化方案(二)

语言: CN / TW / HK

背景

在上一篇文章 [Xcode]一种低成本的link耗时优化 方案中提到了一个优化方案,但是仍然存在一些副作用。在一次编译LLVM模块时发现,LLVM的Target link仅需要1秒左右。所以这篇文章会以LLVM的c-index-test作为标准继续探索link耗时优化空间。

找出差异

先列出几组头条iOS工程的link耗时数据 - 未优化前,模拟器带lto

  • 先前的优化,模拟器合并静态库带lto

  • c-index-test的link耗时

第一轮优化后,耗时从56.8秒变成29.9秒。优化后大部分时间在reslove symbols阶段。而编译c-index-test的reslove symbols阶段仅需要996ms毫秒

c-index-test的参数解析时间很短,这其中有什么秘密吗?

通过分析c-index-test的link命令发现。其中对于.a的引用全部是绝对路径。
这样就避免了从library_search_path中去查找对应的libxxx.a文件。从而加快解析速度。
之前的方案 [[Xcode]一种低成本的link耗时优化方案] 中也提到,这个方案之所以可以加快link时间也是因为解析参数的时间变少了。而合并静态库会带来一些副作用。(虽然影响不大)
但是LLVM这种管理方式更加合理。有了之前的基础迁移起来也很快。注意一点就是避免直接把路径拼接到xcconfig中。而是使用-filelist参数。否则,很有可能会导致参数过长。
优化完了解析参数仅用2.4s(1276 archive files),比之前合并静态库还要快(188 archive files)。
如果去掉library_search_path还可以再快一点。为了保证兼容性,暂时保留了library_search_path。
重点是,这个方案仅仅是调整了一下输入参数。没结果没有任何影响。所以也没有合并静态库的副作用。

分析reslove symbols阶段

ld64是开源。虽然不是最新版本,但足够用来分析reslove symbols阶段。准备好代码后经过一番配置后。先用profile调试看看。

可以看出,不知道那块开了4个进程非常耗时。(而c-index-test是没有这几个线程的)这里应该是关键。 接着找到reslove symbols阶段的代码 使用log法对reslove symbols子方法进行耗时分析得出。 由于是调试的原因,时间仅做参考。只看权重。

可以看出buildAtomList和linkTimeOptimize占比较高。
其中buildAtomList没有发现什么异常。
经过调试发现,profile中多出的几个线程就是在linkTimeOptimize创建的。

看一下linkTimeOptimize的代码,如果没有LLVMObjs就直接return。
调试c-index-test发现在这里是直接return的。
也就是说头条工程中link时因为存在LLVMObjs导致发生了linkTimeOptimized优化。
那么接下来在 _haveLLVMObjs = true的地方分析一下那些对象会导致这个flag设置成了true。

结果log显示只有TTShortVideoBusiness这个库会发生问题。 "~/Desktop/Workspace/tt_app_ios/Pods/TTShortVideoBusiness/arch_ios/libTTShortVideoBusiness_Awesome_.a(AWEActionSheetAnimated.o)"

为何TTShortVideoBusiness会开启linkTimeOptimized

因为组件平台发布二进制时,会使用一个壳工程同时打出release包和debug包。
使用头条的壳工程打release包时会开启TLO优化。
而我本地安装的TTShortVideoBusiness也是release版本。那么只要使用debug版本就好了。
经过排查是组件平台的消息队列丢失了一些数据。重新同步后可以安装到正常的debug版本了。
解决掉linkTimeOptimized后测试一下。

reslove symbols阶段耗时减少了7秒左右,但跟c-index-test比仍然有12秒左右的差距。
经过排查发现时间都花在resolveUndefines上面了。

resolveUndefines

这里就不细说了。暂时还没有什么解决办法。仅列出一些有用的信息。
c-index-test需要解析200个undefine符号。
头条iOS需要在这个阶段解析5000+undefine符号。
在resolveUndefines会一直循环解析直到undefine符号的数量等于0。
所以继续减少link时间,只能想办法减少undefine符号的数量了。

20200520更新

本来以为这个方案没有副作用。但是还是有"副作用"的。

在头条的工程中,如果更换了Other_Link_Flags的一些参数顺序后,有一定几率会导致sqlite发生问题。在debug启动时会命中secguardSDK的断言。

经过调查发现是这个WCDBOptimizedSQLCipher库引起的,原本应该用-all_load或者-force_load去加载这个库中的所有符号。却只用了-ObjC 和-l"WCDBOptimizedSQLCipher"。结果导致在调整Other_Link_Flags的一些参数顺序后,有些符号没有load进来。所以在运行时发生了异常。

同理还有libasr.a也存在这种问题。

对于这种库建议尽早切换成-force_load的方式来加载。

关于App-Infra DevOps 团队

App-Infra DevOps团队作为公司的移动研发中台,致力于优化公司各业务的研发和交付过程中的质量、成本、安全、效率和体验。到目前为止我们已经有超过上百个业务接入,1万+人日常使用;平均每天支撑 3000+研发任务顺利交付;建设了业界领先且可能是国内最大的构建集群,每天运行20万+次构建任务。

我们期待更多有热情和创造力的同学加入字节跳动,为字节打造业界最领先,最高效的生产工具。

我们的工作内容主要包括:

  • 提供任务管理,工程管理,开发调试,交付流程支持的一站式同开发平台。同时提供Native版及Web版,目标成为下一代端云协同的研发基础设施。
  • 客户端 IDE 云化的探索性项目,目的是提供一整套线上编码、调试环境,让用户打开浏览器就可以进行代码开发,满足不同场景的需要,提升研发效率。

加入我们

北京-高级iOS开发工程师 — 终端技术 https://job.toutiao.com/s/2YHxkXW

职位描述

1、负责公司产品的通用技术研发和性能优化,完成高质量编码和测试工作 2、框架维护,通用工具开发等 3、设计良好的代码结构,不断迭代重构; 4、前沿技术研究,承担重点、难点的技术攻坚

职位要求

1、本科及以上学历,计算机、通信等相关专业 2、具有扎实的编程功底,良好的设计能力和编程习惯 3、熟练掌握Objective-C,C++,熟悉Swift的优先 4、熟悉bash/python/ruby/js的优先

上海-高级iOS开发工程师 — 基础技术 https://job.toutiao.com/s/2YHm8a7

职位描述

  1. 负责公司客户端产品的通用基础技术研发,完成高质量编码和测试工作
  2. 通用研发工具开发、通用框架开发等
  3. 设计良好的代码结构,不断迭代重构
  4. 前沿技术研究,承担重点、难点的技术攻坚

职位要求

  1. 本科及以上学历,计算机、通信等相关专业
  2. 具有扎实的编程功底,良好的设计能力和编程习惯
  3. 熟练掌握Objective-C,C++,熟悉Swift的优先
  4. 熟悉前后端相关和跨平台技术、熟悉bash/python/ruby/js等语言和相关框架的优先

杭州-高级iOS开发工程师 — 基础技术 https://job.toutiao.com/s/2YHdPAE

职位描述

  1. 负责公司跨平台相关技术研发,完成高质量编码和测试工作
  2. 负责通用跨端框架及周边工具开发等
  3. 设计良好的代码结构,不断迭代重构
  4. 前沿技术研究,承担重点、难点的技术攻坚

职位要求

  1. 本科及以上学历,计算机、通信等相关专业
  2. 具有扎实的编程功底,良好的设计能力和编程习惯
  3. 熟练掌握Objective-C,C++,熟悉Swift的优先
  4. 熟悉前后端相关和跨平台技术,熟悉bash/python/ruby/js等语言和相关框架的优先