从分钟到秒:抖音如何通过动态库优化实现高效构建
如果无法正常显示,请先停止浏览器的去广告插件。
1. 从分钟到秒:抖 如何通过
动态库优化实现 效构建
:张星宇
程师
字节跳动 / 软件
演讲
2. 录
01 背景
02 动态库的优势与挑战
03 核
04 收益与规划
作流程与原理
3.
4. 01
背景
构建耗时的瓶颈与思考
5. 抖
90% 的构建属于增量构建,其耗时直
接决定研发效率
增量构建代码改动量极 ,缓存命中
率 ,付出的等待时间是否合理
超过 200
时
6. 增量构建的耗时分布
编译
3%
源码到
进制 MachO 的过程
调试启动
13%
LLDB 启动调试准备的过程
产物安装
38%
App 传输到 机的过程
链接
47%
动态库或可执
件的
成过程
7. 关键阶段耗时统计度量
具链,字节跳动终端技术
8. 耗时的影响因素
改动的代码,以及受这些 参与链接的静态库越多, Xcode 安装时会
改动( 静态库的体积越 的哈希。有改动的
如头
件)影响
的代码越多,速度越慢
,符号
总数越多,链接速度越慢
多、
件体积越
较
件
件越
,安装
速度越慢
LLDB 会加载、解析并缓存
动态库或可执
件的原
数据。改动的动态库或可
执
件越
,调试启动
越慢
编译
链接
产物安装
调试启动
9. 常
成本
,原
Preview
基于 Swift 的动态能
时替换
中等。稳定性有保障,但是
流程 ,偶尔出问题
动态库
,适配和改造成本
实现运
不
,只 持动态库或 SPM 程,
持 cocoapods 静态库。不 持断点。
中等,底层动态库改动会沿着
依赖链路反向传递 中,部分场景会退化为普通编译
低,依赖 ld-prime 链接器,
不可定制 低,依赖 Xcode 内部多个
服务,不可定制
10. 理想中的
程复杂度
关的常数级耗时
11. 02
动态库的优势与挑战
动态库虽好,但使
要注意
12. 动态库的优势
链接
构建系统只需要重新链接动态库,其它部分
LLDB 只加载解析动态库,其它部分
调试启动 Xcode 只需要传输动态库,其它部分
产物安装
改动,
改动,
动态库链接速度快
动态库安装速度快
改动,调试启动速度快
13. 理想中的
程架构
App
App
组件 1
Dev.framework
组件 3
组件 4
组件 2
组件 …
组件 1 组件 2 组件 5
组件 6 组件 … 组件 1000
Core.framework
组件 1000
14. 静态库转动态库的问题
静态库 B
组件 A
组件 D
不可少:动态库对完备性具有极
静态库 C
B
C
A
C
D
B
D.framework
A.framework
不宜多:动态库对其依赖的静态库具有 “吸附性”
的要求
15. 动态库对架构的要求
组件 C
组件 F
组件 A
组件 D
组件 G
组件 E
组件 H
CFJ.framework
组件 C、F、J 理论上可以构成动态库
将来组件 C 需要依赖组件 D 怎么办
除 找到依赖完备的若
这对 程架构提出了极
组件 B
组件 J
组件,否则 法链接成功
的要求,很难实现并保持
16. 动态库相互依赖
Dev.framework Core.framework
missing symbol A missing symbol B
… …
defining symbol B defining symbol A
动态库不可能存在相互依赖关系,任意静态库有可能强制转换动态库么?
17. 03
FocusLink 作流程
18. 动态查找符号
libPod1.a
编译:
Missing Symbol A
static linker
参数: -U/-undefined dynamic_lookup
Dev.framework
运
:
dyld
Dynamic lookup symbol A
Dev.framework
Symbol A in Core.framework
Dynamic lookup symbol A
Core.framework
Dev.framework
19. 运
时崩溃
Novel.o
App
libBook.a
NSLog()
main.o
Novel.framework
20. 符号排查
可以 nm 命令排查 进制 件中的符号
发现 libBook.a 中确实有这个符号,但是链接进 Novel.framework 以后就没有了
21. Symbol Resolver
buildAtomList
链接器解析符号本质上是 套 度优先(BFS) 算法,
只加载有必要的符号和 件,尽可能确保产物简单
resolveAllUndefines
Input File
查找
加载
deadStripOptimize
linkTimeOptimize
undefined list
记录
symbol
22. 理想架构
Core.framework
lib1.a
lib3.a
lib2.a
Dev.framework
lib4.a 对 lib6.a 有依赖关系
lib3.a
lib3.a 和 lib4.a 不再参与
Core.framework 的链接
lib4.a
lib4.a
lib5.a
lib6.a
转换为 Dev 动态库依赖 Core
动态库
23. 符号计算
Core.framework
lib1.a
lib3.a
lib5.a
Dev.framework
lib2.a
lib3.a
lib4.a
lib6.a
X
lib4.a
lib3.a 和 lib4.a 不再参与
Core.framework 的链接
但实际情况是 lib6.a 中的
符号也可能受到影响,产物
可能会丢失符号
如果丢失的符号恰好被lib4
依赖,就会造成运 时崩溃
24. 符号计算
Core.framework
lib1.a lib2.a
lib3.a lib4.a
Dev.framework
lib3.a
lib4.a
lib5.a
lib6.a
如何确保 Core.framework
完整性
对于任意 lib3.a 或 lib4.a
中的未定义符号,如果
lib1-2.a 和 lib5-6.a 中能
提供,必须确保这些符号出
现在 Core.framework 中
25. 链接器改造细节
调整符号可
性
LTO 性能优化
libPod.a
+
Framework
Reserved symbols
确保符号加载
确保符号保留
26. 链接器改造细节
未定义符号加载流程,抽象成 search 函数以便复
保留符号,避免被优化消除
27. 链接器改造细节
性,运
强制调整符号可
时被 dyld 解析
修复 LTO bug 优化性能
28. 符号计算
计算 Core 动态库需要保留的符号
遍历 Dev.framework 中的未定义符号
检查 Core.framework 中是否包含这个符号
如果包含,则 Core.framework 需要保留这
些符号。对 Dev.framework 的处理也同理
29. 构建整体流程
30. 动态库选择策略
持
31. 代码适配
规范 Bundle 的获取
式
dyld 遍历 image 时适配多动态库
32. 04
总结与展望
33. FocusLink
案收益
优化前
80
单位:秒
优化后
80
60
60
40
30
20
0
10 10
产物安装 调试启动
1
链接
34. 未来展望
• 按照理想架构拆分更多动态库,优化
业务上多个独
尾场景下的性能
的模块,形成固定的动态库配置,减少 Core 动态库的复杂度、改动频率和改动成本
Core 动态库改动后,多个
相互依赖关系的动态库,彼此可以并
链接,发挥多核性能
• 跨模块不合理符号依赖关系识别标记,推动架构优化治理与准
原本业务上模块之间在链接层
独
的依赖难以识别并拦截,容易形成历史债务
配置动态库后,可以在链接时完成依赖隔离和治理,对于特殊情况也可以明确以
• 线性耗时优化为常数级耗时的思路,形成共识,在研发全链路推
式临时豁免
落地
程加载、编译、调试、索引等研发活动,先定位耗时瓶颈,再推动常数化耗时的优化策略落地
对于 pod install、
名单的
35.
36. THANKS
模型正在重新定义软件
Large Language Model Is Redefining The Software