Rust中的库与二进制文件:如何打包而不损失性能

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

一个包,两个实体:神话还是现实?

\n\n

想象一下:你用Rust编写了一个用于处理大数据的出色库。它速度快如闪电,安全且优雅。但您不仅需要供其他开发者使用的API,还需要一个现成的CLI工具,用于测试库或在管道中使用它。这就引出了一个在Stack Overflow上获得数千次浏览的经典问题:“Package with both a library and a binary?”(同时包含库和二进制文件的包)。

\n\n

对于Rust新手来说,这听起来像是一个难题。对于经验丰富的开发者来说,这是一个关于优化项目结构和性能的任务。在本文中,我们不仅会回答这个问题,还会重点关注优化和性能——这是Rust最受喜爱的两个特点。

\n\n

我们将分析如何正确组织这样的包,避免代码重复,加快编译速度,并使您的二进制文件真正快速。开始吧!

\n\n

包结构:Cargo.toml决定一切

\n\n

在Rust中,一个Cargo包可以同时包含库(lib)和多个二进制文件(bin)。这不是魔法,而是一个简单的文件夹结构约定。让我们看一个最小的例子。

\n\n

创建一个项目:

\n\n
cargo new my_tool --lib
\n\n

现在修改Cargo.toml文件,明确指定我们同时拥有库和二进制文件:

\n\n
[package]\nname = "my_tool"\nversion = "0.1.0"\nedition = "2021"\n\n[lib]\nname = "my_tool_lib"\npath = "src/lib.rs"\n\n[[bin]]\nname = "my_tool"\npath = "src/main.rs"\n\n[dependencies]\n
\n\n

文件夹结构将如下所示:

\n\n
my_tool/\n├── Cargo.toml\n├── src/\n│   ├── lib.rs      // 库\n│   └── main.rs     // 二进制文件\n
\n\n

很简单,对吧?但魔鬼在于细节。如果您只是将库中的代码复制到main.rs中,您将得到重复代码和性能问题。需要更聪明地操作。

\n\n

正确的方法:二进制文件使用库

\n\n

二进制文件应该将库作为外部crate导入。即使它们位于同一个包中。这不仅减少了重复,还允许Rust编译器通过跨crate优化(LTO)更好地优化代码。

\n\n

src/lib.rs示例:

\n\n
pub fn calculate(data: &[u64]) -> u64 {\n    data.iter().sum()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_calculate() {\n        assert_eq!(calculate(&[1, 2, 3]), 6);\n    }\n}\n
\n\n

src/main.rs示例:

\n\n
use my_tool_lib::calculate;\n\nfn main() {\n    let numbers = vec![10, 20, 30];\n    println!("总和: {}", calculate(&numbers));\n}\n
\n\n

注意:我们使用my_tool_lib——这是我们在Cargo.toml中为库指定的名称。编译器会自动将它们链接到一个二进制文件中,同时保持模块化。

\n\n

性能优化:LTO、内联和基准测试

\n\n

现在结构已经准备好,我们来谈谈重点——如何从您的包中榨取最大性能。因为如果您正在创建一个数据处理工具,每一微秒都至关重要。

\n\n

1. 启用链接时优化(LTO)

\n\n

LTO允许编译器跨crate边界优化代码。没有LTO,库和二进制文件中的函数会分别优化。有了LTO,它们会被当作一个整体优化。

\n\n

在Cargo.toml中添加:

\n\n
[profile.release]\nlto = true\ncodegen-units = 1\n
\n\n

这会增加编译时间,但会使二进制文件明显更快。对于教育网站pythonlib.ru来说,需要强调:LTO是Rust生产构建的标准。

\n\n

2. 使用内联提示

\n\n

如果库中有关键函数,

博客

书籍推荐