在 Rust 版本中默认为
opt-level = 3
。在很多情况下,这不是预期的行为。根据库的不同,这可能会显着增加二进制大小,同时可能比 opt-level = 2
慢。我找到了使用该库覆盖最终项目的这些默认值的选项:
[profile.release.package.my_package]
opt-level = 2
在许多情况下,库作者最清楚默认情况下应在哪个优化级别构建库,因此库本身能够覆盖此默认值是有意义的。我尝试使用:
[profile.release]
opt-level = 2
在图书馆内
Cargo.toml
,但这没有任何效果。有什么方法可以让它发挥作用,还是只有最终项目才能做到这一点?有没有办法实现更细粒度的优化级别,例如每个文件还是需要将每个文件放入单独的库中?
这是不可能的。
opt-level
是 Cargo profile 的一部分,它控制编译器的行为。配置文件是在构建时从默认配置文件、任何最终用户自定义配置文件以及用于执行构建的 Cargo.toml
设置的任何 overrides中选择的。您在库的
Cargo.toml
中设置的覆盖仅影响属于该库的二进制文件。
如果您选择分发动态库,您可以为分布式共享对象/DLL 设置
opt-level
,但这会阻止您的库的函数内联到用户代码中。
您的最佳选择(就
opt-level
而言)是建议用户(在文档、自述文件或其他地方)opt-level = 3
会导致代码膨胀,并建议使用opt-level = 2
来代替。 Bevy 是一个提供类似建议的流行项目,它建议设置opt-level
以使调试构建更快。
根据库的不同,这可能会显着增加二进制大小,同时可能比
慢。opt-level = 2
如果您担心最终的二进制大小,还有其他方法可以减少生成的代码量。您可能会建议用户启用 LTO,这可以减少代码大小,但代价是额外的编译时间。
另一个常见的罪魁祸首是泛型,特别是非常长的函数的单态化。如果性能允许,您可以选择创建调用公共非泛型函数的泛型函数存根。例如:
fn foo<S: AsRef<str>>(input: S) {
// Some long, complex function.
// ...
}
为调用
foo()
的每种不同类型生成代码(&str
、String
、Cow<str>
等)。您可以通过强制所有此类调用遵循相同的路径来降低重复单态化的成本:
fn foo<S: AsRef<str>>(input: S) {
foo_str(input.as_ref());
}
fn foo_str(input: &str) {
// Complex logic goes here.
// ...
}
这会为
foo()
的每个单态化生成非常少的代码,并且所有生成的函数都会调用共享代码路径,从而导致更小的二进制文件和可能更好的指令缓存使用。 YMMV,当然;首先分析并关注导致最膨胀的功能。