Julia 优化构建指南:基于 PGO、ThinLTO 与 BOLT 的三阶段流水线解析 📅 发布时间:2026/9/19 3:50:33 👁 浏览次数: Julia 优化构建指南基于 PGO、ThinLTO 与 BOLT 的三阶段流水线解析【免费下载链接】juliaThe Julia Programming Language项目地址: https://gitcode.com/gh_mirrors/ju/juliacontrib/optimized是 Julia 官方维护的一键优化构建目录它将 Profile-Guided OptimizationPGO、ThinLTO 和平台支持时的BOLT 二进制重排整合为一条可复现的构建流水线取代了此前的contrib/pgo-lto、contrib/bolt与contrib/pgo-lto-bolt三个分散的 Makefile见 contrib/optimized/README.md。本文以该目录的 README.md 为骨架结合其 Makefile 源码完整讲解流水线的阶段划分、全部可调参数、自定义采集 profile 的实操流程与平台限制帮助你构建出更适合自身工作负载的 Julia 发行版。快速上手一条命令产出优化构建从仓库根目录执行make -C contrib/optimized -j8 all构建结果位于contrib/optimized/optimized.build流水线会先从 BinaryBuilder 下载 clang、lld 与 LLVM 工具链BOLT 需从源码构建随后以系统镜像system image与包镜像package image的构建过程作为 PGO 训练负载产出插桩版 Julia用得到的 profile 配合 ThinLTO 重新构建 Julia若启用了 BOLT还会对libLLVM、libjulia-internal、libjulia-codegen三个共享库插桩、以镜像构建作为训练负载采集 profile并据其重写二进制。默认配置下BOLT 在 Linux x86-64 与 AArch64 上默认开启USE_BOLT1其他平台默认关闭PGO 与 ThinLTO 默认均开启。仅需要 PGOLTO 时设置USE_BOLT0仅需要 BOLT 时设置USE_PGO0 USE_LTO0此时跳过 stage 1。三阶段流水线的整体结构Makefile 将整个流程划分为 0/1/2 三个 stage源码证据见 contrib/optimized/Makefile阶段产出目录默认作用stage 0contrib/optimized/toolchain构建/下载统一工具链clang、lld、llvm-tools、zstd、BOLTstage 1contrib/optimized/pgo-instrumented.build以-fprofile-generate构建的插桩版 Julia其镜像构建即 PGO 训练负载stage 2contrib/optimized/optimized.build使用合并后的 profile ThinLTO 构建的最终优化版 JuliaStage 0统一工具链stage0目标在toolchain目录中安装 clang、LLVM、lld、llvm-tools 与 zstd并在启用 BOLT 时安装install-BOLT。这里有一个值得注意的细节工具链构建完成后Makefile 会用llvm-objcopy将libclang_rt.profile-*.a中的[cd]tors段重命名为init/fini_array段——因为 lld 不会自动做这个转换否则 profile 构造函数不会被执行PGO 插桩将静默失效contrib/optimized/Makefile。Stage 1插桩构建PGO 数据采集stage1以-fprofile-generate$(PGO_PROFILE_DIR)与-Xclang -mllvm -Xclang -vp-counters-per-site6编译JULIA_CPU_TARGET固定为generic该阶段只为收集编译器 profile无需为发布版的每个 CPU target 生成镜像见 contrib/optimized/Makefile。依赖构建如 llvm-tblgen产生的插桩工具进程会被隔离到deps-profiles/目录避免污染 Julia 工作负载的 profilecontrib/optimized/Makefile。Stage 2优化构建stage2使用-fprofile-use$(PGO_PROFILE_FILE)、-fltothin与-fuse-ldlld编译并附上-Wno-backend-plugin -Wno-profile-instr-unprofiled -Wno-profile-instr-out-of-date等告警抑制参数。原因在 Makefile 注释中写得很清楚CMake 的 configure 探测可能与 profile 函数冲突抑制这些告警才能让-Werror的编译探测真正检验所需选项否则 LLVM 可能因 profile 哈希不匹配而静默丢弃-fPIC与-fno-semantic-interpositioncontrib/optimized/Makefile。Stamp 文件的断点续传机制各阶段以 stamp 文件stage0、stage1、stage2、bolt等标记完成状态已完成的 stage 不会重复执行。Stamp 不追踪选项变更修改优化设置时必须使用全新构建目录或先make clean清除 stamp。注意make clean只删除 stamp 与合并后的 PGO profile不会清理编译产物也不会撤销 BOLT 重写contrib/optimized/Makefile。全部构建选项与默认值README 给出的选项表摘自 contrib/optimized/README.md变量默认值含义USE_BOLTLinux x86-64 与 AArch64 上为 1其余为 0是否执行 BOLT 各阶段USE_PGO1构建并插桩 stage 1再以其 profile 优化USE_LTO1以 ThinLTO 构建 stage 2STAGE1_CPU_TARGETgeneric插桩构建的 CPU targetSTAGE0_BUILD$(CURDIR)/toolchain工具链构建目录STAGE1_BUILD$(CURDIR)/pgo-instrumented.build插桩构建目录STAGE2_BUILD$(CURDIR)/optimized.build优化构建目录使用要点README 明确说明命令行传入的构建变量会透传给各 stage 构建包括 stage 2 的JULIA_CPU_TARGET覆盖构建目录时必须使用绝对路径STAGE2_BUILD也可以直接指向源码检出目录用于就地打包测试用例test_source_checkout_as_stage2验证了此路径见 contrib/optimized/test_makefile.pymake print-profile-artifacts会输出值得归档的 profile 通配符列表相对于源码根目录CI 据此归档以复现构建或二分定位回归contrib/optimized/Makefile。关于 BOLT 默认值在 Makefile 中的判定逻辑仅当HOST_OSLinux且HOST_ARCH为x86_64或aarch64时默认开启其余平台macOS、Windows、其他架构默认USE_BOLT0contrib/optimized/Makefile。自定义 PGO 训练负载分阶段采集 profile默认训练负载是构建 Julia 自身系统镜像 包镜像。若希望针对自己的应用场景优化可分开执行各 stage在间隙补充自定义工作负载make stage1 # 可选make clean-pgo-profiles 丢弃构建自带的 PGO 训练数据 ./pgo-instrumented.build/julia my-workload.jl make top # 查看合并后的 PGO profile make stage2说明插桩版 Julia 执行任意脚本时profile 会写入profiles/目录变量PGO_PROFILE_DIRmake top调用llvm-profdata show --topn50并经llvm-cxxfilt反混淆展示耗时前 50 的函数用于评估 profile 质量它同时是发现计数器溢出的手段——README 提示运行过重的脚本可能导致计数器溢出contrib/optimized/Makefile新增或更新的.profraw文件会使merged.prof的合并步骤重新执行依赖关系为$(PGO_PROFILE_FILE): stage1 $(PGO_PROFRAW_FILES)见 contrib/optimized/Makefilemake clean-pgo-profiles只清空 PGO profile不影响 BOLT 的 profilemake clean-profiles则两者都清。自定义 BOLT 训练负载插桩、训练与重写启用 BOLT 后stage2只构建到需要被重写的库为止STAGE2_TARGETS即julia-src-release、julia-symlink、julia-libccalltest、julia-libccalllazyfoo、julia-libccalllazybar、julia-libllvmcalltest见 contrib/optimized/MakefilelibLLVM.so、libjulia-internal.so、libjulia-codegen.so此时仍是未插桩的原始库。要插入自己的训练负载流程为make bolt-train # 可选make clean-bolt-profiles 丢弃构建自带的 BOLT 训练数据 ./optimized.build/julia my-workload.jl make bolt # 合并 profile 并重写库BOLT 子流程各目标源码证据见 contrib/optimized/Makefile目标行为bolt-originals为三个共享库保存一份*.original副本插桩与最终重写都从这份原始库出发bolt-instrument用llvm-bolt --instrument --instrumentation-file-append-pid生成插桩库保留原始 mtime 以避免触发下游重建bolt-train以插桩库构建系统镜像与 stdlib 包镜像——这就是 BOLT 的训练负载bolt-merge用merge-fdata合并profiles-bolt/下的原始.fdata文件bolt用合并后的 profile 以llvm-bolt重写三个库关键约束与实操细节必须在最终重写前采集 BOLT profile——重写完成后库已不再是插桩版本make bolt-train之后的库仍是插桩状态此时运行自定义负载即可收集数据新增/更新的原始.fdata会触发对应的合并步骤重新执行bolt-merge: bolt-train $(BOLT_PROFRAW_FILES)make restore-originals用*.original恢复重写前的库并保留其 mtime之后make bolt可再次重写确认无误后make delete-originals删除备份再打包——备份删除后再次恢复或重写都需要重新构建库contrib/optimized/Makefile插桩与重写都使用普通页对齐-no-huge-pages与 Julia 常规构建一致。BOLT 重写参数与已知限制Makefile 中BOLT_ARGS借鉴了 rust-lang 的 opt-dist 工具注释标明出处为 rust 仓库src/tools/opt-dist/src/bolt.rs见 contrib/optimized/Makefile主要包括-reorder-blocksext-tsp函数内基本块重排-reorder-functionscdsort函数级重排-split-functions-split-all-cold将函数代码切分为热/冷区域-jump-tablesmove跳转表移到独立段-icfall折叠相同代码identical code folding-update-debug-sections重写后更新 DWARF 调试信息-dyno-stats输出优化统计。针对 Julia 的两个特殊处理值得注意libjulia-internal.so跳过-split-strategycdsplit与--use-old-textBOLT 对 computed goto计算跳转支持不完整而 libjulia-internal 恰好大量使用该特性对应 llvm/llvm-project#89117 与 #89508 两个上游 issue。重写时对非libjulia-internal的库额外传入--use-old-text -split-strategycdsplit即复用旧文本段以减小库体积、采用 cache-directed sort 三路切分contrib/optimized/Makefile-skip-funcs.\*apply_cl.\*跳过apply_cl相关函数BOLT 名称常带转义后的参数签名实际缓解手段是重写时不使用--use-old-textcontrib/optimized/Makefile。此外BOLT 重写需要 ELF relocation 信息启用 BOLT 时构建会向BOLT_LDFLAGS注入-Wl,--emit-relocs并为 GCC 追加-fno-reorder-blocks-and-partitiongcc ≥ 8 必需。这些 flag 会透传至src/Makefile中libjulia-internal与libjulia-codegen的链接规则见 src/Makefile 与 src/Makefile。千万不要 strip 被 BOLT 重写过的共享库——这对应上游 llvm/llvm-project issue #56738strip 会破坏重写结果contrib/optimized/README.md。平台支持与前置条件README 的 Platforms 一节说明了平台现状contrib/optimized/README.mdmacOS流水线沿用了 macOS PGOLTO 工具链设置包括 Xcode 的链接器ld64与 SDK。Makefile 中的细节是stage-0 clang 不带 Xcode需通过xcrun --show-sdk-path定位 SDK链接显式使用 Xcode 的 ld64 并指定 stage-0 的libLTO.dylib因为新 SDK 的 text stubs 有时无法被 stage-0 的 ld64.lld 读取LLVM 静态库也改用llvm-libtool-darwin创建contrib/optimized/MakefileWindows链接器设置lld 的 MinGW 驱动仅是准备性的并不代表 Windows 构建已受支持BOLT 默认仅限 Linux x86-64 与 AArch64因为 BOLT 只能重写 ELF 库其他平台显式设置USE_BOLT1可以覆盖默认值但默认值或显式设置都不能替代目标平台上的验证。若希望完全绕开 BOLT 的源码构建可走 BinaryBuilder 的 jll 制品路径deps/BOLT.mk中当USE_BINARYBUILDER_BOLT1时改用bb-install安装预编译的 BOLT jll否则才从 llvm-project 源码BOLT.version固定llvmorg-22.1.8分支及对应 SHA以 CMake 构建bolt目标deps/BOLT.mk。工程化保障测试与可复现性contrib/optimized附带了一个不启动任何真实构建的单元测试test_makefile.py用 mock 的llvm-profdata、merge-fdata、llvm-bolt脚本验证流水线行为运行方式python3 contrib/optimized/test_makefile.py。它覆盖了 8 种 PGO/LTO/BOLT 开关组合的完整流程、选项透传如 stage 1 必须带-fprofile-generate且JULIA_CPU_TARGETgenericstage 2 必须带-fprofile-use与-fltothin、插桩失败即中断整条链、restore-originals保留 mtime 且可再次重写、以及clean后不重新 configure 已有目录等行为contrib/optimized/test_makefile.py。对于可复现性make print-profile-artifacts输出的 profile 归档清单PGO 的profiles/merged.prof与 BOLT 的profiles-bolt/*.merged.fdata意味着只要归档了 profile 与构建配置后续即可复现或定位性能回归。结语从默认一键构建到定制优化总结下来contrib/optimized的核心价值在于把工具链统一 → PGO 插桩训练 → ThinLTO 优化 → BOLT 重排整合为一条可用 stamp 断点续传、可分离执行、可用测试保障的流水线。默认的make -C contrib/optimized -j8 all适合大多数用户针对特定工作负载的深度优化则遵循分开跑 stage、清掉默认 profile、跑自己的负载、再 merge 与重写的套路即可同时务必留意 BOLT 的 ELF 平台限制、clean的边界不清理编译产物、不撤销重写以及重写后不可 strip的硬性约束。【免费下载链接】juliaThe Julia Programming Language项目地址: https://gitcode.com/gh_mirrors/ju/julia创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考