之前一直做的是Android开发,如今到了鹅厂做IOS分布式编译的研究,想在提高IOS编译性能上有所突破,以下是自己在查找资料,浏览博客,Google来的一些总结,总会有认知上及理解上的不到位。也希望能与各位一起探讨一下提高IOS编译性能上的一些解决方案。
一、对C/C++的编译流程的认知
图一
图二
二、提高编译性能的现有解决方案:
(1)Icecream
基于Distcc的分布式编译工具,实现思想:一台主控制机加上若干台工作站,主控机把编译任务分配给工作站,主控机会根据工作站CPU的空闲情况进行合理分配。主控机和工作站会自己进行网络通讯,从而知道网络内有多少台机器愿意分布式编译,用户不用像distcc那样,指定工作站的名字。
优点:避免分别工作到负载重的服务器,优先分配到工作比较轻的服务器,所以会出现文件不在本机编译的情况
(2)Distcc-DMUCS
Distcc作为经典的分布式编译工具,Distcc 在日常工作中常为我们使用来解决大型项目在单一工作站上编译较慢的问题。其主要用于对 C, Object C 以及 C++ 代码进行并行编译,将可以并行的编译任务分布于编译集群中的各个工作站,有效利用各机器资源,达到整体编译性能的成倍提升。
实现原理:distcc 借助了 C/C++ 编译驱动程序的以下特点:
1、 cpp(C 预处理器 ) [cpp [arguments] .c 源文件输入 .i 中间文 件输出 ]
2、 ccl(C 编译器 ) [ccl .i 中间文件输入 .c 源文件输入 [arguments] –o.s 汇编文件输出 ]
3、 as( 汇编器 ) [as [arguments] –o .o 目标文件输出 .s 汇编文件输入 ]
这个在本地做过预处理的 ASCII 源文件及其他命令行选项即可唯一确定一个目标文件,而与此任务在哪台机器上运行无关,通过分发这种任务到各个节点,即可消除对头文件的依赖。同理 distcc 通过在任务的分发节点做链接来消除对库文件的依赖。
缺点:负载均衡算法过于简单,distcc 的代理进程对各个工作机当前的负荷没有感知,分发预处理文件的唯一依据是主机出现在 DISTCC_HOST 环境变量中的次序,主机名越靠前,就会得到更多的编译任务,然而当编译场中某些机器性能过差,整体编译性能会显著下降,当阻塞 Make 运行的编译任务运行在这些机器上的时候,这种性能变化尤为明显。如图1所示: DMUCS是一种实现负载均衡的解决方案,正好可以弥补Distcc负债均衡算法过于简单的缺点。在编译集群中增加监控各工作机工作量的DMUCS监控程序,动态检测和平衡编译机的负载。事实上DMUCS 是一个实现于 distcc 之上的动态平衡和任务分布程序。
优点:
l 支持多用户同时编译,扩展性好,可以很好处理新增的负载。
l 支持多种操作系统所组成的编译集群。
l 可以使用具有多处理器(多核)编译主机的所有处理资源。
l 可以充分使用具有不同处理速度的编译主机,使整体编译性能达到最优。
l 可以保证参与编译的主机不会由于编译任务而产生超负载的情况。
l 考虑到了编译主机上由非编译任务所引起的负载情况。
l 支持从编译集群中动态的增加或者移除编译主机。
采用 DMUCS 的负载均衡来配置编译集群,当其启动并行数量为 6 的编译任务时,理想状态下 DMUCS 会首先将任务优先分布到高性能的编译机进行编译。当高性能编译机上的负载饱满以后,余下的编译任务将被分布到低性能的编译机上进行编译。由此,每个编译机资源将得到最大限度的利用,编译性能得到最大提升。如图2所示:
(3)IncrediBuild for Windows
IncrediBuild是一款编程开发工具,可加快C/C++ 的编译和创建速度。能无缝集成到Visual Studio开发环境中,采用Xoreax 的多线程处理技术,不必改变项目文件的代码。
IncrediBuild通过高效的并行计算实现加速创建应用,它利用网络上空闲的CPU资源,将PC和服务器网络转换到被称作“虚拟超级计算机”的私有计算云中。任务进程被分发到远端CPU资源用于并行计算,动态缩短创建应用时间。
(4)LLVM—Clang
LLVM用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time)。
LLVM本身并不是编译器,只是一套用于开发编译器、解释器等程序语言相关工具的库,主要聚焦于编译器后端功能,如代码生成、代码优化、JIT等。
XCode用的编译器是Clang。Clang是一个基于LLVM开发的C/C++/Obj-C编译器,有一套独立的前端,后端直接采用LLVM。还有一个较为早期的相关项目LLVM-GCC,是一个将GCC的前端嫁接到LLVM之上拼接而成的一个完整的编译器。
测试证明Clang编译OC代码的速度为GCC的三倍,其生成ATS所耗内存仅为GCC的20%左右。
(5)修改配置文件
<!--[if !supportLists]-->l <!--[endif]--> 使用普通的“DWARF”而不是“DWARF with dSYMFile”作为“Debug Information Format”
这一项设置的是是否将调试信息加入到可执行文件中,改为DWARF后,如果程序崩溃,将无法输出崩溃位置对应的函数堆栈,但由于Debug模式下可以在XCode中查看调试信息,所以改为DWARF影响并不大。这一项更改完之后,可以大幅提升编译速度。
需要注意的是,将Debug Information Format改为DWARF之后,会导致在Debug窗口无法查看相关类类型的成员变量的值。当需要查看这些值时,可以将Debug Information Format改回DWARF with dSYM file,clean(必须)之后重新编译即可。
不要使用 –O4 标识编译项目代码,也不要使用带有-O4编译的静态链接库。因为这样Clang会开启链接时优化(LTO),这会延缓链接速度。最多使用-O3 。
提高XCode编译时使用的线程数
XCode默认使用与CPU核数相同的线程来进行编译,但由于编译过程中的IO操作往往比CPU运算要多,因此适当的提升线程数可以在一定程度上加快编译速度。
<!--[if !supportLists]-->l <!--[endif]-->将Build Active Architecture Only改为Yes
在工程对应Target的Build Settings中,找到Build Active Architecture Only这一项,将Debug时的No改为Yes。
这一项设置的是是否仅编译当前架构的版本,如果为No,会编译所有架构的版本。需要注意的是,此选项在Release模式下必须为No,否则发布的ipa在部分设备上将不能运行。这一项更改完之后,可以显著提高编译速度。
(6) 增量编译
普通编译器必须编译整个模块或者程序,但是增量编译器只需重新编译程序代码中更改的那部分代码,仅输出相对应的结果(通常该编译器的目标语言是字节码bytecode)。在有效利用先前编译结果的基础上,增量编译器避免了重复编译整个源文件(大多数代码都没有改变)。对于大多数增量编译器来说,编译程序中的一小部分改动部分几乎是即时性的。增量编译器减少了传统编译器的编译细度,使得编译器可以追加或者替换更小的部分。如VisualAge C++编译器。
(7)FASTBuild
Github上的一个开源项目,支持并行编译、增量编译及分布式编译,同时支持OS X平台的本地及增量编译,分布式编译正在测试中。支持OS X平台的OC分布式编译。FASTBuild尚处于开发测试阶段,但从测试数据来看,其对于编译性能的提高效果十分显著。
特点:体量轻,跨平台,高效率,运行占用较小内存
三、 提高编译效率的解决方案个人思路
(1) 增量编译与分布式编译相结合
增量编译和分布式编译都是提升编译性能的解决方案,如果能够将两者相结合的话对于编译性能的提高将上一个层级。但就目前来讲还没有开源的解决方案。主题思路是将分布编译后的.o目标文件回传到主机并存储,在调试修改后将修改过的.c文件进行分布式部署到相应的服务器进行编译,回传后与存储在主机的.o文件进行链接形成可执行文件。
(2)内存虚拟磁盘,纯内存IO
存的读取速度比硬盘的读取速度高得多,将编译过程中的IO操作部署到内存中可以达到提升程序编译性能的目的。
存在问题:由于被编译程序将放在内存中进行,一旦设备断电将导致所有数据丢失。
四、 总结
经过数天来的资料查阅得出,能够提高程序编译性能的方案不少,诸如:IncrediBuild ,icecream,distcc等,但能够在Xcode上进行IOS分布式编译的解决方案除了Xcode4.X以前的版本支持的Distcc以外,目前没有非常高效的解决方案。FASTBuild作为正在开发中的高性能分布式编译项目,将作为后期主要关注对象,并尝试着运用现有版本进行程序编译性能优化。