这是一篇关于在 Docker 中构建 Rust 网站时遇到编译速度慢问题的详细记录及优化过程的文章,主要内容如下:
- 问题描述:作者的网站主要由单个 Rust 二进制文件提供服务,每次修改都需要重新构建静态链接二进制文件、复制到服务器并重启网站,这很不理想,所以想改用容器部署。但快速的 Rust 构建与 Docker 并不简单。
基础:Docker 中的 Rust
- 简单方式:通常的方法是创建多个 Docker 阶段,先在构建阶段构建 Rust 程序,然后在最终阶段复制二进制文件。但这种方式每次有变化都会重新构建所有内容,耗时约 4 分钟。
- 更好的缓存:Luca Palmieri 的
cargo-chef
工具可帮助预构建所有依赖项作为 Docker 构建缓存的单独层,只在代码库更改时重新编译代码库,而不是依赖项。但实际效果并不理想,大部分时间仍在最终二进制文件的构建上。
rustc
的耗时分析- 使用
cargo --timings
和rustc
的自我剖析功能-Zself-profile
来获取更多信息,发现主要耗时在链接时优化(LTO)和LLVM_module_codegen_emit_obj
上。 - 调整 LTO 和调试设置(
debug
和opt-level
),发现禁用所有优化时编译最终二进制文件约需 50 秒,"fat" LTO 比完全禁用 LTO 耗时约 4 倍。
- 使用
深入探究 LLVM
- 通过
-Ztime-llvm-passes
和-Zllvm-time-trace
获取 LLVM 的剖析信息,发现优化(OptFunction
)和内联(InlinerPass
)是耗时的两个部分。 - 尝试调整 LLVM 的内联参数(如
--inlinedefault-threshold
等),发现降低内联阈值可使编译时间缩短,但难以确定最佳值。 - 分析具体函数的优化时间,发现一些来自依赖项(如
serde_json
、tokio_postgres
等)和core
的函数优化时间较长,尤其是一些异步函数和闭包。
- 通过
优化尝试及结果
- 对一些耗时的异步函数和闭包进行拆分、合并和使用
Pin<Box<dyn Future>>
等操作,可使编译时间有所减少。 - 综合多种优化措施,如减少内联、拆分昂贵函数、移除依赖项的泛型等,最终将编译时间从约 175 秒减少到 9.1 秒,包括启用
-Zshare-generics
和切换为 debian 基础镜像等。
- 对一些耗时的异步函数和闭包进行拆分、合并和使用
总结与展望
- 工具在解决问题时效果良好,文档也足够帮助相对缺乏经验的人改进代码库。
- 指出一些仍需解决的问题,如异步函数调用图的编译时间、
rustc
对core::ptr::drop_in_place
的处理等,并提出可能的改进方向。 - 建议在某些情况下设置
opt-level = 0
可能就足够了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。