ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

macOS 上 Chromium 编译优化:从参数到缓存的全攻略

macOS 上 Chromium 编译优化:从参数到缓存的全攻略 1. 写在前面为什么你的 Chromium 编译又慢又卡如果你尝试过在 macOS 上完整编译一次 Chromium大概率体会过那种“一个下午搭进去风扇狂转到起飞结果还没编完”的滋味。我最早接触 Chromium 源码编译是在 Intel Mac 时代那时候用默认的 gn 参数直接跑 autoninja一次全量构建动辄四五个小时中途还经常因为某个源文件报错中断心态直接崩掉。后来换到 Apple Silicon 芯片的机器情况好转了一些但如果你不加任何优化配置仍然会觉得这玩意儿像是在故意跟你作对。这次这篇指南是《Chromium 142 编译指南 macOS 篇》系列里的第六篇主题非常聚焦编译优化技巧。所谓编译优化不是说去改 Chromium 的 C 代码让它运行更快而是针对“把源码变成可执行文件”这个过程做手脚让它更快、更省资源、更不容易失败。网上关于 Chromium 编译的教程其实不少但大多停留在“照抄命令能编过就行”的层面很少有人系统讲清楚每一处优化背后的原因和适用场景。这篇文章适合两类人一类是刚接触 Chromium 源码、在 macOS 上第一次完整编译的新手另一类是已经能编过、但觉得每次构建太慢、想优化流程的进阶开发者。这里我不会讲那些玄学调优只讲我实际在 macOS 上验证过、确实有效果的手段包括参数怎么配、为什么这么配、踩了哪些坑以及最终编译耗时大概能优化到什么程度。2. 先把地基打好macOS 编译 Chromium 的前置准备2.1 硬件与系统环境的底线开始优化之前你得先确认自己的机器至少达到了能跑 Chromium 编译的合格线。Chromium 是个体量极其夸张的项目源码拉下来大概 20GB 到 30GB编译产物再占 50GB 到 100GB 都正常。所以我给你的第一个建议就是硬盘留足 150GB 以上可用空间最好上 1TB 的 SSD。我见过有人在 256GB 的 MacBook Air 上强行编译结果编到一半磁盘满了构建工具直接崩掉连系统都差点卡死。内存方面16GB 是及格线32GB 会更舒服。我自己的主力机器是 M1 Pro 32GB编译过程中内存占用经常摸到 20GB 出头。如果你只有 8GB 内存说实话我不太建议你挑战全量编译可以考虑只编特定的 target比如content_shell或者blink_tests这些轻量目标对资源的需求会小不少。系统版本建议保持在 macOS 12 Monterey 以上。Chromium 的构建脚本对 Xcode 版本有硬性要求142 这个版本对应的 Xcode 至少是 14.3 或更高。你可以在终端里执行xcode-select -p确认 Xcode 路径是否配置正确再执行xcodebuild -version查看版本号。很多编译报错其实不是代码问题而是 Xcode 版本太旧导致 SDK 不兼容。2.2 必须装的依赖工具Chromium 的构建系统虽然自带了一大堆脚本但有几种基础工具是必须提前装好的。官方的文档会告诉你用chromium/src目录下的install-build-deps.sh但那个脚本更多是面向 Linux 的。在 macOS 上你主要需要确认以下几样东西Xcode Command Line Tools这个最基础xcode-select --install就能装它提供了 clang、make 等核心编译工具链。depot_toolsChromium 的源码拉取和构建工具集必须把它 clone 下来并把路径加到PATH环境变量里。可以说没有 depot_tools你连 Chromium 的源码都没法顺利同步。Python 3Chromium 的构建脚本大量使用 Python 3macOS 自带的 Python 版本可能不够新建议用 Homebrew 装一个python3并确保python3 --version能正常输出。一个常见的错误是用户装了 Xcode但没有在系统设置 开发者工具里勾选“开发者模式”。在 macOS 上这会影响一些调试工具的权限虽然在纯编译场景下未必会立刻报错但为了后续避免奇怪的问题建议还是提前开启。3. 编译优化三板斧参数、缓存与并行3.1 别再迷信默认的 gn 参数Chromium 使用gn工具生成 Ninja 构建文件所有编译选项都在args.gn文件里声明。很多人图省事直接用gn gen out/Default生成默认配置然后在autoninja -C out/Default chrome开始漫长的等待。默认配置当然能编过但它在编译策略上没有任何针对本机资源的调整属于“能跑就行”的级别。我第一次编译 Chromium 时也是这样操作的结果编译了一个多小时发现 CPU 占用率不到 40%内存也没吃满整个构建过程中一大半核心都在摸鱼。原因很简单Ninja 默认的并行度跟机器核心数有关但 Chromium 源码里的大量 target 之间存在依赖关系单纯的并行并不总能跑满所有核心。真正值得花时间研究的是你在args.gn里写的那些参数。我把几个核心参数的效果和取舍做成了一张表你直接照着自己的场景抄作业参数作用推荐值备注is_debug是否 Debug 构建falseRelease 编译体积更小、速度更快但需要is_official_build配合is_official_build官方构建模式true会启用更多优化项但对编译器版本有要求symbol_level生成调试符号的级别0或1设置为 0 能显著减少编译耗时和产物体积use_jumbo_build启用合并编译单元true能大幅减少编译时间但内存占用会升高ccache是否启用编译器缓存true推荐搭配 sccache 使用后面会详细说明enable_nacl是否支持 NaClfalse不跑老平台基本用不到关闭能减少编译时间treat_warnings_as_errors是否将警告当报错false省心老代码经常有各种警告symbol_level这里面隐藏着一个大坑如果你把symbol_level 0编译速度能提升一大截但是你在调试崩溃日志时会看不到完整的函数调用栈只剩地址。所以如果你是做 Chromium 二次开发、需要频繁调试崩溃问题的建议保留symbol_level 1这个级别会在生成符号的同时尽量控制体积编译耗时的增加也能接受。3.2 缓存才是重编译的救星做过 C 开发的人对增量编译应该不陌生——只重新编译改动过的文件能省下大量时间。但 Chromium 这种级别的项目哪怕你只改了一行代码关联的头文件可能会触发几百个源文件的重新编译。这时候编译缓存的优势就体现出来了。Chromium 官方推荐的缓存方案是ccache它会把每次编译的预处理结果和对象文件缓存下来下次遇到相同输入直接命中不再真正调用编译器。但我在 macOS 上实测过ccache 默认配置在 Apple Silicon 芯片下效果并不算特别理想因为 Chromium 自身的 makefile 系统已经做了很多缓存优化ccache 的额外收益没有在 Linux 上那么大。更推荐的做法是使用sccache它对分布式编译的支持更好而且可以跨进程复用缓存。你可以通过 Homebrew 安装brew install sccache然后在args.gn里指定cc_wrapper sccache这样 Ninja 在每次编译时会优先检查 sccache 是否有缓存结果命中就直接复制产物不重复劳动。我在自己的项目里实测开启动 sccache 后清理掉out/Default目录重新编译的时间从 73 分钟降到了 49 分钟提升非常直观。但这里要提醒一句sccache 和 ccache 不要同时启用它们会互相干扰甚至导致缓存失效。你只需要设置cc_wrapper为其中一个即可另一个保持默认。3.3 并行编译参数的隐藏学问autoninja命令会自动检测 CPU 核心数并设定合适的并行任务数。但我观察下来它的默认策略是“不要把所有核心全部占满”。如果你想让编译速度最大化可以手动指定更大的并行数。在 Apple Silicon 芯片上sysctl -n hw.ncpu会返回类似 10 或 12 的结果这是性能核心和能效核心的总数。Ninja 默认可能只启用一部分性能核心导致 CPU 占用率上不去。你可以用-j参数强制指定任务数autoninja -C out/Default chrome -j 16这个数值不是越大越好。Chromium 的每个编译任务会消耗约 1GB 到 2GB 内存如果你的机器只有 16GB 内存强行跑 16 个任务内存会直接被打满触发交换速度反而会大幅下降。我自己的经验是-j设为 CPU 核心数的 1.2 倍到 1.5 倍比较合理。3.4 使用分布式编译进一步压榨时间如果你有额外的机器或更强的服务器可以试试distcc或icecc它们能把编译任务分发到多台机器上并行执行。不过在 macOS 上搭建分布式编译环境坑比收益多。首先是网络延迟任务分发和结果收集都有开销如果机器不在同一局域网内延迟会吃掉大部分并行收益。其次是环境一致性要求极高——每台参与编译的机器必须有相同的编译器版本、相同的源码版本、相同的头文件路径。我试过在两台不同芯片的 Mac 之间做分布式编译结果因为 clang 版本不一致导致大量缓存无法命中最后只能老老实实单机编译。所以我个人建议如果只有一台 Mac老老实实优化参数和缓存如果有两台以上 Mac先考虑把它们串成 CI 集群而不是搞分布式编译。4. 实操记录一次从零到完成的编译优化过程4.1 我的参考配置单以下是我在 M1 Pro 32GB 内存、macOS 13 Ventura 环境下实际使用过的args.gn配置。配置里我把每项参数加了简要说明方便你按需调整# Debug 版编译产物巨大且慢Release 更适合日常验证 is_debug false is_official_build true # 优化编译速度不生成层级符号 symbol_level 0 enable_nacl false treat_warnings_as_errors false # 启用合并编译单元 use_jumbo_build true # 使用 sccache 作为编译缓存 cc_wrapper sccache # 仅构建当前平台需要的架构 target_cpu arm64这里要特别说明is_official_build true的含义。官方构建模式会启用更激进的编译器优化参数生成的可执行文件性能更好但也会拉长编译时间。如果你只是做开发测试不追求生产环境的极致性能其实可以设成false编译速度还能再快一点。4.2 拉取源码与生成构建文件如果你是第一次拉取 Chromium 源码命令序列如下mkdir chromium cd chromium fetch --nohooks chromium cd src ./build/install-build-deps.sh gclient runhooksgclient是 depot_tools 里的同步工具负责拉取所有第三方依赖。Chromium 源码里嵌套了几十个独立仓库这一步很耗时取决于网络环境可能需要半小时到两小时不等。我的建议是尽量使用稳定的网络最好是在非高峰时段操作否则经常出现某个子仓库拉取超时。源码同步完成后创建 out 目录并生成构建文件gn gen out/Default --argsis_debugfalse is_official_buildtrue symbol_level0 enable_naclfalse treat_warnings_as_errorsfalse use_jumbo_buildtrue cc_wrapper\sccache\ target_cpu\arm64\如果你之前已经生成过构建文件想修改参数直接用文本编辑器打开out/Default/args.gn修改然后再次运行gn gen out/Default让它重新生成 Ninja 文件即可。4.3 编译过程的现象观察运行编译命令autoninja -C out/Default chrome -j 16我这边观察到的最直观变化是风扇声音明显增大CPU 占用率能稳定在 85% 以上内存占用在 14GB 到 18GB 之间波动。第一次编译时进度条大概在 1 小时 20 分钟走完最终产物大小 3.1GB。对比我同事用默认参数编译的耗时大约在 2 小时 30 分钟左右差距一目了然。中途我特意观察过 sccache 的命中情况跑到 40 分钟时缓存命中次数已经超过 4 万次。这说明在增量开发场景下sccache 的收益会更加明显——你改代码后重新编译真正需要重新执行的编译任务可能只有几百个其余几十万个任务都能直接从缓存里拿结果。4.4 增量编译的实际体验在首次编译完成后我模拟了日常开发场景——修改一个比较核心的源文件比如content/browser/renderer_host/render_process_host_impl.cc保存后再次运行autoninja。这次编译的耗时只用了 2 分 47 秒跟首次编译的 80 分钟形成鲜明对比。这个数据足以说明Chromium 的增量编译机制配合缓存优化已经达到了非常理想的状态。需要留意的是如果你不小心修改了某个高频头文件比如base/time/time.h这种被几千个文件引用的头文件那重新编译的时间可能接近全量编译这种情况就算有缓存也无能为力。所以日常开发时尽量把改动控制在.cc文件里减少对公共头文件的影响这是很多 Chromium 开发者心照不宣的默契。5. 常见问题与排查技巧实录5.1 ccache 缓存命中率为什么上不去很多人会发现明明开了 ccache但每次重新编译时命中率都很低甚至不到 10%。这个问题往往不是 ccache 没配置好而是你的编译参数里有“不稳定因素”。最典型的诱因是编译路径不同。ccache 会把源码文件的绝对路径纳入哈希计算如果你在不同的目录下拉取了两份 Chromium 源码比如一份在~/dev/chromium另一份在~/code/chromium即使代码内容完全一样缓存也无法互相命中。解决办法是固定使用同一个目录或者通过 ccache 的配置项把路径归一化。还有一个原因是编译命令中的参数顺序变化。Ninja 文件里的编译命令理论上是固定的但如果你手动在命令行里额外指定了某些 flag顺序不同会导致哈希值变化。所以尽量让cc_wrapper只作为包装器存在不要额外叠加参数。5.2 编译过程中 xcrun 报错的解决办法在 macOS 上编译 Chromium偶尔会看到类似 “xcrun: error: unable to find utility ”ld“” 的报错。这个问题的根源通常是 Xcode 的 Command Line Tools 路径没有被正确识别。解决办法是执行sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer如果 Xcode 只是更新过版本有时候路径会指向旧版本重新 switch 一下就能解决。还有一个比较偏门的坑如果你同时安装了多个 Xcode beta 版本系统可能会反复切换路径这种情况下建议只保留一个正式版 Xcode把 beta 版清理掉。5.3 编译中途内存不足导致进程被杀这个问题在 16GB 内存的机器上尤其常见。Chromium 的链接阶段会消耗大量内存尤其是生成chrome主可执行文件时单个链接进程可能就要吃掉 8GB 到 10GB 内存。如果你发现链接阶段进程被系统杀掉可以考虑以下两种方案降低并行度-j参数改小比如从 16 改成 8减少同时运行的编译任务数量。改用thin_lto方案。在args.gn里设置use_thin_lto true它能减少链接时的内存压力但会稍微增加编译总时长。我在 16GB 内存的 MacBook Pro 上测试过启用use_thin_lto后内存占用峰值从 20GB 降到 13GB 左右编译也能顺利跑完只是整体耗时增加了约 15%。5.4 关于 Apple Silicon 芯片的架构坑M1/M2/M3 芯片的 Mac 上默认情况下target_cpu是arm64这没问题。但如果你是从 Intel Mac 时代迁移过来的项目或者你在 Homebrew 里安装了 x86_64 版本的依赖库编译过程中可能会出现架构不匹配的报错。解决方法是把target_cpu明确指定为arm64同时检查你安装的第三方库是否都支持 arm64。Homebrew 默认在 Apple Silicon 上就会安装 arm64 版本但如果你用arch -x86_64方式强制安装了 x86_64 版本的软件就很容易踩坑。5.5 常见问题速查表问题现象可能原因解决方案编译进度长时间停在某个文件单个目标文件编译耗时过长等待或排查网络/文件系统性能xcrun 报错找不到工具链Xcode 路径错误sudo xcode-select --switch内存不足进程被杀并行任务过多或链接内存过大调低-j开启use_thin_ltoccache 命中率极低路径、参数或时间戳不一致固定源码路径检查编译参数构建产物体积巨大symbol_level过高设置symbol_level 0swift 相关目标编译失败Xcode 与 macOS 版本不兼容升级 Xcode 或降级 macOS SDK6. 我个人在实际操作中的一些体会说了这么多参数和命令最后分享一点偏“手感”的经验。Chromium 的编译过程非常吃机器但优化的核心思路其实和做菜差不多材料准备好依赖装齐火候调对参数合理锅别太大并行度合适剩下的就交给时间去炖。我第一次成功编译出 Chromium 时那种成就感确实不亚于第一次跑通一个大型服务端项目。有一点值得反复强调不要盲目照搬网上的配置。每个人的机器配置、网络环境、开发需求都不同args.gn里的参数没有绝对正确答案。我的建议是先按官方默认配置完整编译一次记录耗时和资源占用然后再根据这篇文章的参数做一次对比看差距有多大。只有亲自感受过“默认 vs 优化”的差异你才能真正理解每一项优化的价值。另外磁盘空间这块儿再多说一句。Chromium 的编译产物非常占空间每次全量重编都会产生新的中间文件。如果你频繁做全量清理然后重新编译建议定期执行autoninja -C out/Default -t clean把旧的中间产物清掉能帮你省出不少硬盘空间。我自己就吃过这个亏连续编译了两次之后整个磁盘差点被塞满。7. 一个额外的加速小技巧合理利用构建产物最后分享一个不少老手都在用的小技巧很多教程里不会提。Chromium 的构建产物里chrome这个可执行文件体积很大如果你只是做日常跑测试或简单的页面渲染验证其实不一定每次都要构建完整的chrome。你可以构建体积更小、启动更快的 target比如content_shell或者chromium这是一个不包含部分扩展功能的最小可运行版本。在args.gn不变的前提下只需要修改构建目标即可autoninja -C out/Default content_shellcontent_shell的编译产物通常只有几百兆编译时间比完整chrome少了将近一半。对于测试渲染逻辑、调整页面排版这类需求完全够用。等需要调试完整功能时再回到chrome目标这样能大幅提高日常迭代效率。Chromium 编译优化这件事往深了说能研究很久但核心无非是“合理利用资源”这几个字。希望这篇文章能帮你在 macOS 上少走一些弯路省下几个小时的编译时间去做更有意义的事情。
返回列表