Chromium 定制版 PGO 实战:Chrome 与 V8 Builtins 两套体系以及打包踩坑

📅 2026/7/3 3:24:40 👁️ 阅读次数 📝 编程学习
Chromium 定制版 PGO 实战:Chrome 与 V8 Builtins 两套体系以及打包踩坑

本文基于一次真实升核(M148 / V8 14.8.x)打包与 PGO 训练整理


前言:为什么 PGO 会「编着编着就缺文件」

切到 Official Release + PGO Phase 2 后,常见报错:

../../v8/tools/builtins-pgo/profiles/x64-rl.profile

needed by snapshot_blob.bin -> gen/v8/embedded.S

missing and no known rule to make it

这往往不是 snapshot 坏了,而是 Chrome 主程序 PGO 与 V8 Builtins PGO 是两套机制:只提交了 Chrome 的*.profdata,却默认打开了 V8 builtins 优化,构建在 schedule 阶段就失败。


一、两套 PGO:别混成一个「PGO 包」

Chrome PGOV8 Builtins PGO

优化对象

chrome.dll等 C++

V8 builtins(mksnapshot/ snapshot)

训练产物

*.profdata

x64-rl.profile

训练工具

tools/pgo/generate_profile.py

官方下载或v8/tools/builtins-pgo/generate.py

Phase 2 开关

chrome_pgo_phase=2

v8_enable_builtins_optimization=true(Phase 2 默认常开)

is_official_build=true

→ chrome_pgo_phase=2

→ v8_enable_builtins_optimization 默认为 true(若未显式关闭)

→ Windows x64 需要 x64-rl.profile

Chrome profdata 有了 ≠ V8 profile 有了。


二、Chrome PGO:Phase 1 与 Phase 2

Phase 1:插桩采集

  • GN:chrome_pgo_phase=1
  • 编译带-fprofile-generate
  • 插桩版浏览器跑 benchmark →.profrawllvm-profdata mergeprofile.profdata

python3 tools/pgo/generate_profile.py -C out/PGOPhase1

需要 完整源码树 + Phase 1 输出目录,不能只有二进制。

Phase 2:用 profile 优化

  • -fprofile-use=...profdata+ ThinLTO
  • 链接chrome.dll通常最慢
  • CI 若每次 clean 全量,每次都像第一次全编

三、Phase 1 链接坑:升核后 fork 与上游 API 类型不一致

Phase 1 除了「能编过、能跑 benchmark」,还可能遇到 链接阶段 才暴露的问题。下面是一次 M148 升核 +chrome_pgo_phase=1时的案例(扩展工具栏 ViewModel),与 V8 profile 无关,但 不做修复就过不了 Phase 1,也就训不出 Chrome profdata。

3.1 现象

args.gn中设置:

chrome_pgo_phase = 1

# fork 定制宏已开启(如扩展工具栏仍走 vector 顺序语义)

执行:

autoninja -C out/Release chrome

chrome.dll链接失败,lld-link 报 undefined symbol,符号栈与base::flat_tree/flat_set析构相关——表面看像 STL 或 libc++ 链接问题,实际是 接口返回类型与底层实现不一致 在 PGO 插桩 下被放大。

3.2 根因(148 上游 vs fork 历史行为)

148 上游把ExtensionsToolbarViewModel::GetAllActionIds()改成了返回const base::flat_set<ActionId>&,与ToolbarActionsModel::action_ids()对齐。

定制 fork 在 产品宏分支 下仍保留ToolbarActionsModel::action_ids()返回std::vector<ActionId>&(扩展工具栏顺序语义依赖 vector,而非 sorted flat_set)。

若 ViewModel 仍声明返回flat_set&,实现里却return actions_model_->action_ids()

  1. 编译器对vectorflat_set做隐式转换,构造 临时flat_set
  2. 函数返回的是 临时对象的引用(本身已是危险写法)
  3. PGO Phase 1 插桩 下,flat_tree析构路径 无法如 Release 那样内联
  4. 链接器找不到对应符号 →chrome.dll链接失败

非 PGO 或 Phase 0 构建有时能「侥幸过」或表现不同;Phase 1 是更严格的照妖镜。

3.3 修复思路(与上游分叉的最小对齐)

在 fork 宏分支 让声明、实现与底层 同一类型——返回const std::vector<ActionId>&,直接转发action_ids(),避免任何 vector→flat_set 临时对象:

#ifdef FORK_EXTENSIONS_TOOLBAR /* 示意:你们实际的 fork 宏名 */

const std::vector<ToolbarActionsModel::ActionId>&

ExtensionsToolbarViewModel::GetAllActionIds() const {

return actions_model_->action_ids();

}

#else

const base::flat_set<ToolbarActionsModel::ActionId>&

ExtensionsToolbarViewModel::GetAllActionIds() const {

return actions_model_->action_ids();

}

#endif

HasAnyExtensions()在 fork 分支改为 直接读action_ids().empty(),不要再经可能触发转换的GetAllActionIds()

bool ExtensionsToolbarViewModel::HasAnyExtensions() const {

#ifdef FORK_EXTENSIONS_TOOLBAR

return !actions_model_->action_ids().empty();

#else

return !GetAllActionIds().empty();

#endif

}

头文件同步改返回类型,并加注释说明:禁止在 fork 路径返回flat_set&,否则 PGO 链接可能再炸。

3.4 测试建议

做法

链接

chrome_pgo_phase=1autoninja -C out/Release chrome,确认 chrome.dll 链接通过

功能

启动浏览器:扩展工具栏图标显示、拖拽、溢出菜单

回归

Phase 0 / Phase 2 各编一次,确认无新增链接错误

3.5 对 PGO 流程的启示

升核 → 先过 Phase 1 链接 + 能启动

→ 再 generate_profile.py 训 profdata

→ 再 Phase 2 + V8 profile

Phase 1 链接问题 和 Phase 2 缺 x64-rl.profile 是 不同阶段、不同层 的故障:

阶段典型错误性质

Phase 1

lld-link undefined symbol(flat_set/flat_tree)

fork 与 148 API 类型不一致 + 插桩

Phase 2

missing x64-rl.profile

V8 builtins PGO 文件缺失

升核合并时,建议对flat_set/span/ 容器类型变更 做专项 grep:凡 fork 仍用 vector 的模型,上层 ViewModel 不要声明成 upstream 的 flat_set&。


四、V8 Builtins PGO:官方包 vs 自训

4.1 官方 profile

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/{V8四段版本}/meta.json

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/{V8四段版本}/x64-rl.profile

版本来自v8/include/v8-version.h(如14.8.178.4),不是 Chrome 产品号。
meta.jsonrevision需与DEPSv8_revision一致 再用。

4.2x64.profilevsx64-rl.profile

  • Windows 正式构建:x64-rl.profile
  • Linux/macOS + Clang:x64.profile

4.3 内网镜像 404、Google 有

多为 镜像未同步chromium-v8-builtins-pgo桶。

4.4 自训难点(摘要)

Windows 要-rl、GN 须与正式 Release 一致、benchmark 环境、每次 bump V8 重做、profile 默认 gitignore。revision 对齐时 优先官方。


五、推荐策略(决策树)

升核 / 出包

├─ Phase 1

│ ├─ chrome.dll 链接过? → 修 fork/上游 API 不一致(如 GetAllActionIds)

│ └─ generate_profile.py → profdata

├─ Phase 2

│ ├─ Chrome:pgo_data_path / 已提交 profdata

│ └─ V8:x64-rl.profile(官方 revision 对齐)或 v8_enable_builtins_optimization=false

└─ CI:确认 gn gen 真正传入 extra_args

务实组合:Chrome profdata 自训;V8x64-rl.profile用官方(revision 一致时)。


六、换机器训练 Chrome PGO 要带什么

  1. 同 commit 完整src
  2. Phase 1 的out/(去掉obj/*.pdb可瘦身)
  3. llvm-profdata
  4. 定制主程序若不叫chrome.exe,需与generate_profile.py期望名对齐(复制/改名)

七、Phase 2 编译慢

场景体验

首次全量

很慢

同目录无改动

增量几乎为 0

CI 每次 clean

每次都慢


八、官方 V8 下载(示例 14.8.178.4)

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/14.8.178.4/meta.json

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/14.8.178.4/x64-rl.profile

放置:

v8/tools/builtins-pgo/profiles/meta.json

v8/tools/builtins-pgo/profiles/x64-rl.profile


九、总结

  1. Chrome PGO 与 V8 Builtins PGO 两套文件、两套流程。
  2. Phase 1 要先过 链接 + benchmark;fork 与 148 返回类型 不一致会在 PGO 插桩下触发 lld-link。
  3. Phase 2 常见缺x64-rl.profile;Windows 勿用x64.profile顶替。
  4. 官方 V8 profile 在 revision 对齐时可直接用。
  5. CI 检查gn gen是否传入v8_enable_builtins_optimization等参数。

代码示例中的FORK_EXTENSIONS_TOOLBAR为脱敏占位符,请替换为你们仓库实际的 fork 宏名。