一、链接时间优化
链接时间优化,链接阶段执行模块间优化
在编译阶段,clang将发出LLVM bitcode而不是目标文件。链接器识别这些Bitcode文件,并在链接期间调用LLVM以生成将构成可执行文件的最终对象。接下来会加载所有输入的Bitcode文件,并将它们合并在一起以生成一个模块。LLVM的LTO机制是通过把LLVM IR传递给链接器,从而可以在链接期间执行整个程序分析和优化。
LTO有两种模式:
- Full LTO是将每个单独的目标文件中的所有LLVM IR代码组合到一个大的module中,然后对其进行优化并像往常一样生成机器代码。
- Thin LTO是将模块分开,但是根据需要可以从其他模块导入相关功能,并行进行优化和机器代码生成。
--- a.h --- extern int foo1(void); extern void foo2(void);
extern void foo4(void);
--- a.c ---
#include "a.h"
static signed int i = 0;
void foo2(void) {
i = -1;
}
static int foo3() {
foo4();
return 10;
}
int foo1(void) {
int data = 0;
if (i < 0) data = foo3();
data = data + 42;
return data;
}
--- main.c ---
#include <stdio.h> #include "a.h"
void foo4(void) {
printf("Hi\n");
}
int main() {
return foo1();
}
按照LTO优化方式:
- 链接器首先按照顺序读取所有目标文件(此时,是bitcode文件,仅伪装成目标文件)并收集符号信息。
- 接下来,链接器使用全局符号表解析符号。找到未定义的符号,替换weak符号等等。
- 按照解析的结果,告诉执行LTO的库文件(默认是libLTO.dylib)那些符号是需要的。紧接着,链接器调用优化器和代码生成器,返回通过合并bitcode文件并应用各种优化过程而创建的目标文件。然后,更新内部全局符号表。
- 链接器继续运行,直到生成可执行文件。
LTO整个的优化顺序为:
- 首先读取a.o(bitcode文件)收集符号信息。链接器将foo1()、foo2()、foo4()识别为全局符号。
- 读取main.o(真正的目标文件),找到目标文件中使用的符号信息。此时,main.o使用了foo1(),定义了foo4().
- 链接器完成了符号解析过程后,发现foo2()未在任何地方使用它将其传递给LTO。foo2()一旦可以删除,意味着发现foo1()里面调用foo3()的判断始终为假,也就是foo3()也没有使用,也可以删除。
- 符号处理完毕后,将处理结果传递给优化器和代码生成器,同时,将a.o合并到main.o中。
- 修改main.o的符号表信息。继续链接,生成可执行文件。
最后生成的可执行文件main的符号表信息:
三、 Dead strip 优化
链接器的-dead_strip参数的作用是:
Remove functions and data that are unreachable by the entry point or exported symbols.
简单来讲,就是移除入口函数或者没有被导出符号使用到的函数或者代码。现在foo4正是符合这种情况,所以,可以通过-dead_strip来删除掉无用代码。
strip -S:移除调试符号(静态库使用) strip:除了间接符号表中使用的符号,其他符号都移除(上架App使用)
Strip Style设置, Release下为All Symbols
All Symbols: 去除所有符号,一般是在主工程中开启。
Non-Global Symbols: 去除一些非全局的 Symbol(保留全局符号,Debug Symbols 同样会被去除),链接时会被重定向的那些符号不会被去除,此选项是静态库/动态库的建议选项。
Debug Symbols: 去除调试符号,去除之后将无法断点调试。
Strip Linked Product:去除不必要的符号信息。Strip Linked Product 选项在 Deployment Postprocessing 设置为 YES 的时候才生效。
Strip Debug Symbols During Copy:将那些拷贝进项目包的三方库、资源或者 Extension的Debug Symbol 去除掉,Release下设置为YES。
Strip Swift Symbols:移除相应 Target中的所有的 Swift 符号,这个选项也是默认打开的。Swift标准库是会打进目标文件的。
四、 Code Generation Options
1、None[-O0]不优化:
在这种设置下, 编译器的目标是降低编译消耗,保证调试时输出期望的结果。程序的语句之间是独立的:如果在程序的停在某一行的断点出,我们可以给任何变量赋新值抑或是将程序计数器指向方法中的任何一个语句,并且能得到一个和源码完全一致的运行结果。
2、Fast[-O1]大函数所需的编译时间和内存消耗都会稍微增加:
在这种设置下,编译器会尝试减小代码文件的大小,减少执行时间,但并不执行需要大量编译时间的优化。在苹果的编译器中,在优化过程中,严格别名,块重排和块间的调度都会被默认禁止掉。此优化级别提供了良好的调试体验,堆栈使用率也提高,并且代码质量优于None[-O0]。
3、Faster[-O2]编译器执行所有不涉及时间空间交换的所有的支持的优化选项:
是更高的性能优化Fast[-O1]。在这种设置下,编译器不会进行循环展开、函数内联或寄存器重命名。和Fast[-O1]项相比,此设置会增加编译时间,降低调试体验,并可能导致代码大小增加,但是会提高生成代码的性能。
4、Fastest[-O3]在开启Fast[-O1]`项支持的所有优化项的同时,开启函数内联和寄存器重命名选项:
是更高的性能优化Faster[-O2],指示编译器优化所生成代码的性能,而忽略所生成代码的大小,有可能会导致二进制文件变大。还会降低调试体验。
5、Fastest, Smallest[-Os]`在不显着增加代码大小的情况下尽量提供高性能:
这个设置开启了Fast[-O1]项中的所有不增加代码大小的优化选项,并会进一步的执行可以减小代码大小的优化。增加的代码大小小于Fastest[-O3]。与Fast[-O1]相比,它还会降低调试体验。
6、Fastest, Aggressive, Optimizations[-Ofast]与Fastest, Smallest[-Os]`相比该级别还执行其他更激进的优化:
这个设置开启了Fastest[-O3]中的所有优化选项,同时也开启了可能会打破严格编译标准的积极优化,但并不会影响运行良好的代码。该级别会降低调试体验,并可能导致代码大小增加。
7、Smallest, Aggressive Size Optimizations [-Oz]不使用LTO`的情况下减小代码大小:
与-Os相似,指示编译器仅针对代码大小进行优化,而忽略性能优化,这可能会导致代码变慢。
五、无用代码删除
直接使用已有工具Snake,需要简单设置下
- 将其中的snake拷贝到目录中,比如$HOME/custom-tool/bin目录下;
- 打开~/.bash_profile文件:vi ~/.bash_profile,在文件最上方加一行:export PATH=HOME/custom−tool/bin/:
HOME/custom-tool/bin/:
HOME/custom−tool/bin/:PATH,然后保存并退出 - 执行source ~/.bash_profile;
- 至此,Snake工具生效。 找到无引用类、方法和协议
六、Objective-C Direct Methods
七、删除项目中未用到的图片
使用LSUnusedResources工具,导入项目查找无用图片,工具下载地址
八、压缩图片
智图 或者 https://tinify.cn/
九、删除项目中重复图片
十、资源动态下发