Rust Cargo 使用总结

5,615 阅读4分钟

文档列表见:Rust 移动端跨平台复杂图形渲染项目开发系列总结(目录)

2019.4.24 更新:多条件(feature)编译

Cargo用于组织Rust项目,比直接用rustc编译多个源文件更方便。本文档介绍我们开发过程中用到的Cargo功能与小技巧,更多信息可参考 The Cargo Book

使用依赖项目的指定git commit

rev表示要用的git commit id,可简写成前7个字符,因为git commit id前7个字符可算出完整的41个SHA字符值。

[dependencies]
gfx-hal = { version = "0.1.0", git = "https://github.com/gfx-rs/gfx", rev = "bd7f058" }
# 或者写成多行
[dependencies.gfx-hal]
git = "https://github.com/gfx-rs/gfx"
version = "0.1.0" 
rev = "bd7f058"

git仓库地址需要支持https访问,如果是http需要额外配置,比较麻烦。

引用本地Rust项目

[dependencies]
hello_utils = { path = "hello_utils", version = "0.1.0" }

path是相对于本项目Cargo.toml文件的被依赖项目的Cargo.toml的位置,填错将找不到文件,且编译报错。详细信息参考The Cargo Book/specifying-dependencies

workspace feature 条件编译的执行路径问题

当cargo workspace中存在多个project时,如果对某一project进行feature条件编译或单元测试,一定要在此项目的Cargo.toml或src路径中执行,否则Cargo会忽悠cargo testcargo build --features=your_feature所指定的feature,这是个容易遇的坑。

default feature 条件编译

[features]
default = ["gfx-backend-metal"]

写在Cargo.toml的default = ["a", "b", "c"]cargo build是默认启用的,即不用加上--features条件。

多条件(feature)编译

语法:--features "条件1 条件2 条件3",每个feature(条件)中间留出空格,示例:

cargo build --features "metal gl" --bin quad 表示启用metalgl两个条件编译去编译quad可执行程序。

cargo c 减少编译耗时

  • 使用cargo check,命令可缩写为cargo c如果只是想验证语法、类型检查等,那么可以直接使用这个命令,它只会调用编译器前端。比cargo build快2倍,比cargo build -—release快6倍。
  • 使用sccache,该工具是Mozilla出品的Rust兼容的编译缓存服务,一般可以获得2倍速度提升。使用cargo install sccache安装sccache,并且在.bashrc中添加环境变量export RUSTC_WRAPPER=sccache
  • 避免LTO。 LTO是链接时优化的缩写。LTO将付出更高的编译时间代价。
  • 控制crate依赖。

如何缓解Rust编译时间长的痛苦

编译优化级别说明

opt level 3和z,哪个性能优化更高?答案:3。 z不管性能,z和s差不多,O2的优化,减少size。

另一种说法:在不同平台上会得到不同结果,-z为size优化,对于cpu code cache小的机器优势比较大,大概就是优化等级开高了代码尺寸变大会导致在某些cache小的机器上性能下降的很厉害。z在inline的优化肯定要少一些。优化这东西是个神坑,不要跳。建议在你的手机上进行profiling。

opt level 参数完整说明见 Properly document and explain opt-levels s and z

[profile.release] debug=true 的讨论

这样改会影响release的性能吗? 好像只是留下符号信息。编译出来大点吧。性能应该不会有啥影响。你可以试试 说起来release带符号 会不会导致行号不准啊

To get the best data from a profiler, you need both optimizations (usually enabled only in release builds) and debug symbols (usually enabled only in debug builds). To enable both, add this to your Cargo.toml: Programming Rust

尝试:

debug = false --release image-20190225103810132

debug = true --release image-20190225104833677

hal api基本没改善,可能是因为多数被inline或zero cost abstraction?

workspace 与其子 project 同时指定 [profile] 配置会引起冲突

package: /Users/michael/Documents/my_project/gles/Cargo.toml workspace: /Users/michael/Documents/my_project/Cargo.toml 同时指定

# The release profile, used for `cargo build --release`
[profile.release]
panic = "abort"
debug = true # `true` for better profiler readability with debug symbols, `false` for sdk client
lto = true
opt-level = 3
overflow-checks = false

warning: profiles for the non root package will be ignored, specify profiles at the workspace root:

测试Rust代码中的Markdown代码段

/// Open the physical device with `count` queues from some active queue family. The family is
/// the first that both provides the capability `C`, supports at least `count` queues, and for
/// which `selector` returns true.
///
/// # Examples
///
/// ```no_run
/// # extern crate gfx_backend_empty as empty;
/// # extern crate gfx_hal as hal;
/// use hal::General;
/// # fn main() {
///
/// # let mut adapter: hal::Adapter<empty::Backend> = return;
/// let (device, queues) = adapter.open_with::<_, General>(1, |_| true).unwrap();
/// # }
/// ```
///
/// # Return
///
/// Returns the same errors as `open` and `InitializationFailed` if no suitable
/// queue family could be found.
pub fn open_with<F, C>(
    &self,
    count: usize,
    selector: F,
) -> Result<(B::Device, QueueGroup<B, C>), DeviceCreationError>
where
    F: Fn(&B::QueueFamily) -> bool,
    C: Capability,
{
    // ...
}

注释中的Examples代码可以用rust-skeptic进行测试,具体做法可阅读rust-skeptic文档。