唯快不破,揭秘安卓 AOSP 系统构建提速 50% 的黑科技

移动开发DevOps

picture.image

汽车操作系统作为硬件与软件的接口,已成为企业核心竞争点,存在迭代升级快、更新频率高的特点,如何妥善解决编译过慢的问题,建设能快速适应业务发展的敏捷 IT 架构,正被越来越多企业关注。

来源 | 火山引擎云原生团队

在软件定义汽车背景下,操作系统已经成为汽车生态发展的灵魂。根据汽车行业深度报告《软件定义汽车,操作系统是汽车之魂》,全球汽车广义操作系统(功能软件、狭义操作系统、中间件)的市场规模在 2030 年将达到 500 亿美元

而在开源操作系统领域,Android 凭借国内丰富的应用生态切入汽车 IVI 系统,在国内车载信息娱乐系统领域已占据主流地位,尤其是各大互联网巨头、自主品牌、造车新势力,纷纷基于 Android 进行定制化改造,推出了自己的汽车操作系统。

随着 AOSP 的再一次走红,一些曾经在智能手机上困扰技术团队的问题也在汽车行业逐渐显现——编译速度一直是 Android 开发效率的瓶颈,然而目前主流的缓存策略、优化方案带来编译速度提升非常有限,并且随着工程结构日益复杂,编译耗时也急剧增加。

所属行业:汽车制造

系统版本:AOSP 13

文件大小:文件数量多(超过 70 万),代码文件大(20GB)

编译用时:单次构建编译产物有 20-40 GB,耗时高达 40-50 分钟

核心痛点:编译用时久,极大影响了开发、迭代效率;串行编译,在高峰期会出现四五十个任务排队,编译耗时长,构建效率低。

以上是某车企实际遇到的情况。在整个 CI 流程中,构建已经成为最为耗时且亟需优化的一个点。考虑到汽车操作系统作为硬件与软件的接口,已成为企业核心竞争点,存在迭代升级快、更新频率高的特点,如何妥善解决编译过慢的问题,建设能快速适应业务发展的敏捷 IT 架构,正被越来越多企业关注。

如何寻求破局之道

针对编译速度过慢的问题,业内有几种常见的方案:

用顶配的机器跑构建任务,不计成本。这个解决方案在汽车行业比较常见,很多车企的技术团队有能力斥巨资购买多台 192c 甚至更高配置的服务器专门用于跑构建任务,而提升机器的配置必然可以提升构建效率。但这种方案也存在一些问题:

  • 配置高的服务器一年成本可达到几百万,这会给企业带来巨大的 IT 支出负担;
  • 整体利用率不高,任何编译构建任务都存在波峰波谷,业务繁忙期服务器可能不够用,业务低谷期也可能出现高配服务器闲置;
  • 构建速度天花板低,虽然短期内可以通过高配机器提升构建速度,但随着项目体量越来越大,当新瓶颈出现后就无法再提速。

将全量编译,改为精准的增量编译。编译过程实际上每次变化的点并不多,如果能够精准细粒度识别已经编译过的中间产物,并且将中间产物保存到缓存中,下次编译构建时不需要全量编译,可以大大提升构建效率。

从单机构建任务改为分布式构建,突破单机性能极限。这个方案类似大数据的 MapReduce 思想,使用空间换时间,将单机任务变为分布式任务执行,即可突破单机瓶颈,用低配的机器就可以跑出比高配机器还要快的性能。

资源池化弹性,企业内资源共享,高效利用构建资源。建设统一资源池能实现高效、灵活的资源利用,各业务团队根据构建需求租用所需的资源,避免了硬件和软件资源的浪费。如果是云上,还可以使用 Serverless 容器算力,在完全按量付费的同时,享受海量的并发资源,不仅提升了资源利用率,降低了成本,也得到了更好的构建体验。

这些方案都能在特定情况下提供比较显著的加速效果,而为了进一步提升 CI 流程的高效和敏捷,我们可以考虑对它们做有机整合。

火山引擎构建加速实践

火山引擎持续交付 CP 提供的构建加速服务(Build Cloud Service),可以帮助汽车等行业的企业摆脱 Android 编译时间过久的困扰。

它基于字节跳动内部沉淀多年的编译构建实践,有机整合了精准增量编译、分布式构建任务、资源池化三种方案,通过分布式缓存、分布式编译等能力加持,极大提升编译构建的效率,减少编译用时。

构建加速方案

picture.image

如上图所示,火山引擎构建加速服务可以将项目的源文件编译任务从单台机器分散到多台机器上,实现分布式编译。它的具体操作方式是由本地的构建加速客户端拦截构建系统的编译命令,将编译请求和相关的文件发送到远端集群,集群侧会重建本地的目录环境进行编译,然后写入远程缓存。

单条编译任务的分布式构建流程如下图所示:

picture.image

编译命令首先被构建加速客户端的拦截器所拦截,转发给本地的常驻进程客户端。客户端会解析该任务需要的所有依赖文件,包括编译工具链和库资源,再将这些依赖文件组装成文件目录树。然后根据命令参数、环境配置、文件目录树,判断能否命中远程编译缓存。如果命中缓存,则直接下载编译产物;否则需要在集群侧编译该任务,再下载编译产物。集群侧处理编译时,会复刻任务的本地编译环境,包括文件目录结构及环境配置。因此客户端需要先查询并上传缺失的文件,以便集群后续重建目录结构,再发送编译请求,等待集群返回的结果并下载编译产物。

技术难点揭秘

1. 如何实现客户端拦截,从而做到无需修改工程代码

常见的构建系统提供了一些支持命令拦截的参数,用以支持分布式构建。设置这些参数以后,可以拦截原生的编译命令,将生成的编译请求转发给本地的构建加速客户端,客户端基于分布式构建的协议,将任务分发到远程的集群处理。

CMake、Xcode 及 AOSP 的 Soong 构建系统都有各自的命令拦截参数,因此在进行构建时,仅需设置少量的环境参数,无需修改用户的构建脚本,就能以远高于本地机器核数的并发度,加速构建过程。

即使用户的构建系统没有类似的命令拦截参数,只需要在生成编译命令的位置添加我们的拦截器路径,也能以较小的工程改动,实现同样的分布式构建效果。

2. 如何实现精准的增量编译

进行分布式构建时,编译的准确性非常重要。主要体现在两个方面:能否准确的命中远程缓存,远程编译的结果是否准确。

为了防止错误地命中缓存,构建加速客户端在收到编译请求后,会基于 Google 的 remote-apis 协议,综合编译任务的命令参数、环境配置、依赖文件目录树等因素计算缓存的 key,判断能否命中编译产物的缓存。仅当这些因素都未发生改变时,才认为当前编译任务和缓存的编译任务环境是一致的,可以复用同一份编译产物,此时可以从远程缓存集群下载编译产物。未命中远程编译缓存时,客户端会向远程集群发送编译的请求,等待下载编译产物。

远程集群在处理编译请求时,会创建一个临时的环境,重建本地的文件目录树,设置和本地相同的编译环境,从而保证编译过程和结果的准确性。

3. 如何获取依赖文件列表

远程集群在执行编译前,需要重建本地的文件目录结构。因此客户端在发送编译请求前,需要准确的获取并上传编译该任务所需的依赖文件。在获取依赖文件时,本地构建加速客户端从源文件开始逐行扫描代码,识别并解析所有的依赖指令,遍历可能的搜索路径,确定任务所依赖的文件,添加到最终的依赖列表中,然后按照深度优先的顺序,递归解析最新找到的依赖文件。当所有依赖指令处理完成后,依赖列表的结果即为任务所需要的全部依赖文件。

4. 如何实现高效的弹性池化资源

建设共享的分布式构建集群,不同业务团队的构建任务可共享使用该集群。同时利用火山引擎的弹性容器 VCI,实现构建资源的快速创建和销毁,从而达成资源高效利用的目标。

picture.image

字节跳动内部加速效果

如前文所述,这套方案孵化于字节跳动内部,支撑着抖音、今日头条、飞书等业务的快速开发和高效迭代。通过反复测试和对比,它也被验证确实能带来明显的加速效果。

以 Pico 业务为例,Pico 基于 AOSP 11 (Android Open Source Project)开发,使用这套方案后,其构建时间由 31 分钟降至 16 分钟,效率提升 达到 48%

picture.image

对于不同场景、不同版本的提效情况,我们也做了测试(基于 48 core ECS 本地服务器),详细数据如下:

picture.image

写在最后

面对数字化升级过程中企业的增长需求,火山引擎云原生团队始终致力于提供丰富的产品和解决方案,帮助用户应对资源效能、敏捷迭代、工程稳定性、质量保障等方面的严峻挑战。

目前这套方案同时支持公有云私有化部署,已在多家头部车企、造车新势力企业落地,我们也希望未来能赋能更多汽车行业、游戏行业、泛互联网行业的企业,助力大型 Android 应用的开发、落地和迭代,为企业发展提供新动能。

欢迎感兴趣的用户扫码咨询、使用!

picture.image

相关链接

[1] 火山引擎: www.volcengine.com

[ 2] 火山引擎 CP:www.volcengine.com/product/cp

[3] 火山引擎 VCI:www.volcengine.com/docs/6460/76908

火山引擎云原生团队

火山引擎云原生团队主要负责火山引擎公有云及私有化场景中 PaaS 类产品体系的构建,结合字节跳动多年的云原生技术栈经验和最佳实践沉淀,帮助企业加速数字化转型和创新。产品包括容器服务、镜像仓库、分布式云原生平台、函数服务、服务网格、持续交付、可观测服务等。

picture.image

picture.image

picture.image

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论