提高编译速度的方法(swift)
文章中,我会针对编译的各个细节进行说明,一旦我们了解了整个编译过程,我们就可以尝试在各个编译过程进行优化,提高编译速度
先了解一下Xcode 编译过程结构
预编译(PreProcessor)
替换所有的宏
查找所有的依赖
处理预编译的指令
编译器(Compiler)
- 转换源码为机器码(汇编)
这是swift的编译过程,具体流程可以参考我的这边文章
https://krela2010.github.io/2020/10/12/LLVM%E5%AD%A6%E4%B9%A0/
汇编(Assembler)
- 将可读代码转为机器码
- 转为Mach-O文件,Mach-O是二进制文件,可以直接在iOS,Mac系统上运行
链接(Linker)
把不同的Mach-O目标文件合并到一起,生成一个可执行文件
Object Files + dylib, .a , .tbd => Single Executable file
加载(Loader)
- 最后一步,把可执行程序加载到内存里,执行.Loader分配内存空间.加载dylibs,动态库
什么是Clean Build
无缓存,全部编译
什么是Incremental Build
针对修改的文件,进行编译
把编译加速分为2个阶段
- 分析和设置编译选项,这个是最直接,最简单的方法
- 理解swift依赖关系,这个会影响到增量编译
Incremental Build
一阶段:修改Build Settings
编译模式(Compliation Mode)
这个配置控制驱动driver
和前端任务frontend jobs
,先了解一下什么是驱动driver
和前端frontend
驱动(Driver):
任务调度,负责决定哪些文件需要编译或重新编译并运行(即作业)来执行编译和链接步骤
前端任务(frontend jobs):
被Driver调用,运行swift-frontend,执行编译,生成PCH
文件,合并模块等.这一步骤相当耗时
Primary File Mode
Driver把编译工作拆分出多个任务,每个任务读取模块中的所有文件,专注于编译主要的目标文件
Primary File Mode
优点是做增量编译Incremental Build
时,只针对过时需要编译的文件做处理,并且也可以同时运行多个编译工作,借助多核
Whole file Mode
Driver只运行一个编译任务,无论多个文件.一个读取所有文件,编译所有文件
Whole file Mode
优点可以结合整个模块做优化,并且避免了Primary File Mode
里编译初期重复工作,缺点是总是全量重编译
结合两种特点,建议Debug使用Primary File Mode
, Release使用Whole file Mode
Xcode默认也是这样配置的
Optimization Mode
优化模式下执行swift编译器,SIL和LLVM耗费了大量的时间和系统资源
非优化模式下,SIL和LLVM也会同样工作(仍会有一些优化处理),但是资源消耗有所下降,
有3种优化类型,
-Onone
代表无优化处理
-Osize
代表优化文件大小
-O
代表优化编译速度
复杂表达式和类型推断
类型推断是swift很实用的功能,可以不用指明类型,但也会增加大量的编译时间做类型推断工作
如何知道一个表达式耗费了大量的时间?
在开发环境,我们一般认为100ms以下是解析一个表达式的正常时间,如果超过100ms,我们要考虑做对表达式做一些优化,如何自动检查表达式的解析时间?
在Build Settings — > Swift Compiler — > Custom Flags — > Other Swift Flags
添加
-Xfrontend -warn-long-expression-type-checking=100
还可以加一些级别例如250ms,500ms档
删除dSYM文件
dSYM文件(debug symbols file),记录App异常信息,存储在dSYM Bundle里,这个文件每次都会在编译时生成
可以Debug关闭,release开启
运行脚本
运行脚本会在你每次编译都要执行一遍,对此做优化
二阶段:swift依赖关系
编译器知道项目的文件依赖关系,可以决定哪些需要顺序编译,哪些可以并行编译
有3条编译规则
规则1
如果函数体发生变化,swift不会重新编译相关依赖
原因
swift是类型安全语言,不关心你的函数体内的变化
规则2
当在文件里添加新函数,新结构体或者新的扩展时,swift会编译文件所有相关依赖
原因
相较于oc,swift是基于文件编译的语言,你可以在你的文件里写任何的类和结构体,所以基于此,swift需要针对新增部分进行重编译
AllExtensions.swift里添加了所有用到的extensions,如果继续增加,相应的所有的Extension的依赖也要重新编译
如果是按照右边图示,把Extensions放到不同文件,只会编译对应文件的依赖
结论
不要把所有工具扩展放到一个文件里,拆分成不同文件
规则3
如果使用framework组件化结构,framework的任意变化都会是app重新编译所有文件
原文:
https://swift.org/swift-compiler/#compiler-architecture
https://bytes.swiggy.com/advanced-techniques-to-speed-up-the-compile-time-in-xcode-27819cb3be59