百度APP iOS端包体积50M优化实践(七)编译器优化
一. 前言
百度APP iOS端包体积优化系列文章的前六篇重点介绍了包体积优化整体方案、图片优化、资源优化、代码优化、无用类优化、HEIC图片优化实践和无用方法清理,图片优化是从无用图片、Asset Catalog和HEIC格式三个角度做深度优化;资源优化包括大资源优化、无用配置文件和重复资源优化,代码优化包括无用类优化、无用模块瘦身、无用方法瘦身、精简重复代码、工具类瘦身和AB实验固化。本文重点介绍编译器优化,在百度APP实践中,编译器优化包括GCC语言编译优化、Swift编译优化、LTO优化、剥离调试符号、剥离符号表、剔除未引用的代码、Asset 优化、C++虚函数优化和三方SDK编译器方向瘦身。此外,我们重点介绍了指令集架构优化、XCode升级优化和Swift内置动态库优化,这三个模块优化的基础原理都涉及到编译器,所以我们在此篇章一起介绍。
百度APP iOS端包体积优化实践系列文章回顾:
2、《百度APP iOS端包体积50M优化实践(二) 图片优化》
3、《百度APP iOS端包体积50M优化实践(三) 资源优化》
4、《百度APP iOS端包体积50M优化实践(四)代码优化》
二. 编译器优化
丨2.1 方案综述
丨2.2 GCC语言编译优化
丨2.2.1 综述
丨2.2.2 Objective C++编译优化
Xcode的默认优化等级是-Os,但我们使用了-Oz优化方式。在WWDC 2019的《What's New in Clang and LLVM》中,链接地址:https://developer.apple.com/videos/play/wwdc2019/409/,详细介绍了这种优化的原理。它通过识别编译单元中的跨函数相同代码序列来减少代码大小。重复的连续机器指令被外联成函数,原始代码序列被替换为外联函数实现相同机器代码的瘦身,但会增加函数调用栈的深度,因此对性能有一定影响。随着时间的推移,iPhone设备的硬件配置越来越高,这种性能损失是可以承受的。下面是一个官方的demo示例来说明-Oz优化的原理。hasse函数和kakutani函数有相同的机器指令,-Oz优化会生成OUTLINED_FUNCTION_O函数,hasse和kakutani指向该函数,从而降低包体积。
丨2.2.3 C&C++编译优化
在iOS端,许多底层模块都是使用C和C++实现的,例如网络库、播放内核、视觉处理和端智能等。同时,这些模块也支持Android和iOS等多个平台。为了实现跨平台,这些模块通常采用Cmake和GN这两种编译工具。Cmake是一种常见的跨平台编译工具,其主要工作方式是通过读取CMakeLists.txt文件中的指令来生成相应的项目文件。而GN编译工具则是Generate Ninja的缩写,是一种替代Cmake的编译工具。它由Google开源,使用C++编写,主要实现交叉编译,并且可以指定输出平台目标。
丨2.3 Swift编译优化
丨2.4 LTO优化
丨2.5 剥离调试符号
丨2.6 剥离符号表
丨2.7 剔除未引用的代码
丨2.8 Asset优化
丨2.9 C++减少虚函数的使用
丨2.10 三方SDK编译器瘦身
三. 指令集架构优化
丨3.1 iPhone常用指令集架构
丨3.2 指令集架构设置
Architectures选项,Build Settings -> Architectures,值为Standard architectures - $(ARCHS_STANDARD),在真机的编译下实质是(armv7和arm64)在模拟器的时候是(x86_64,i386,arm64) Build Active Architectures Only选项,Build Settings -> Build Active Architectures Only,当其值为Yes时,表示只编译当前一个架构,真机的话一般是arm64, 模拟器是x86_64, 如果为No的时,那就是同时编译第一支持的架构; Excluded Architectures选项,Build Settings -> Excluded Architectures,其值是要排除的架构,例如,如果将其设为arm64,表示产物里面没有arm64架构;
丨3.3 去除无用架构
用lipo -info命令查看framework包含的指令集架构信息,如下所示,AbcSDK.framework支持的指令集是x86_64、i386、arm64和armv7;
lipo -info AbcSDK.framework/AbcSDKArchitectures in the fat file: AbcSDK.framework/AbcSDK are: x86_64 i386 arm64 armv7
lipo命令抽取指定架构,如下所示,从AbcSDK.framework抽取出arm64架构,放在AbcArm64,抽取出x86_64架构,放在AbcArmX86_64。
lipo AbcSDK.framework/AbcSDK -thin arm64 -output AbcArm64lipo AbcSDK.framework/AbcSDK -thin x86_64 -output AbcArmX86_64
lipo -info AbcArm64Non-fat file: AbcArm64 is architecture: arm64lipo -info AbcArmX86_64Non-fat file: AbcArmX86_64 is architecture: x86_64
lipo命令合并架构,合并AbcArm64和AbcArmX86_64,生成新的newAbc,按预期newAbc有两个架构x86_64和arm64,用lipo -info命令验证。
// 合成x86_64和arm64lipo -create AbcArm64 AbcArmX86_64 -output newAbclipo -info newAbcArchitectures in the fat file: newAbc are: x86_64 arm64
替换原先的二进制文件,经过上面这些操作将有x86_64、i386、arm64和armv7四种指令集架构的AbcSDK.framework,瘦身变成了只支持x86_64和arm64两种指令集的组件。
mv -f newAbc AbcSDK.framework/AbcSDK
四. XCode升级优化
Meesage send 函数调用占用从 12 bytes 降低到 8 bytes; Retain and release 函数调用占用从 8 字节降低到 4 字节; autorelease优化,移除自动释放省略中的 mov 指令,体积降低 4 bytes;
五. Swift内置动态库优化
iPhoneX 及以下的机型,如iPhoneX、iPhone8、iPhone7,安装包体积减少20M,下载包大小减少10M; iPhoneX以上的机型,如iPhone11、iPhone12,iPhone13没收益,苹果自身做了优化,我们再做这个优化,价值不会体现;
六. 总结
相比于代码优化、资源优化和图片优化,编译器优化在包体积优化中的投资回报率(ROI)是最高的。然而,编译器优化的影响范围也是最大的,因为每个库的编译器配置修改会影响该库的所有代码。因此,必须对优化质量进行严格控制。在百度APP的优化实践过程中,编译器方向的优化成功减少了30M的包体积,实现了自身库的全部收益,此外,按照体积排名的前15个三方SDK也全部实现了此收益。
END
参考资料:
[1]gcc编译器配置:https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
[2]LTO使用方法:https://llvm.org/docs/LinkTimeOptimization.html
[3]XCode :https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW102
[4]What's New in Clang and LLVM:https://developer.apple.com/videos/play/wwdc2019/409/
[5]XCode14介绍:https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes
[6]improve app size and runtime performance:https://developer.apple.com/videos/play/wwdc2022/110363/#
推荐阅读: