Rust 1.98.0:让浮点优化与整数格式化更接近零成本

2026-08-20 44 预计阅读时间: 1 分钟
来源: blog.rust-lang.org AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

Rust 1.98.0 已经进入 stable 通道。这一版本的重点不在语法大改,而在几个很实用的底层能力:浮点类型新增允许代数重排的运算方法,整数可以直接格式化到专用缓冲区,ManuallyDropBox 的交互也获得了更明确的安全保证。

对于追求吞吐量、低分配和可控内存行为的 Rust 项目来说,这些改动值得单独评估,尤其是数值计算、序列化、日志和高频数据处理代码。

先升级工具链

已经通过 rustup 安装 Rust 的项目,可以直接切换到最新 stable:

rustup update stable
rustc --version
cargo --version

如果需要提前验证未来版本,可以使用 beta 或 nightly 通道:

rustup default beta
# 或
rustup default nightly

生产项目仍建议固定并审查工具链版本,先在 CI 和基准测试中验证,再扩大升级范围。

浮点运算:明确告诉编译器可以牺牲严格顺序

浮点数并不满足实数运算中的全部代数性质。例如,下面两种加法顺序可能产生不同结果:

((a + b) + c) + d
(a + b) + (c + d)

Rust 1.98.0 为 f32f64 提供了 algebraic_addalgebraic_subalgebraic_mulalgebraic_divalgebraic_rem。这些方法允许编译器依据实数代数性质重排运算,从而获得类似其他语言中快速数学优化选项的效果,也可能帮助循环向量化。

一个可以直接放进 Rust 项目验证的示例:

fn algebraic_sum(values: &[f64]) -> f64 {
    values.iter().copied().fold(0.0, |sum, value| {
        sum.algebraic_add(value)
    })
}

fn main() {
    let values = [1.0e16, 1.0, -1.0e16, 2.0];
    let result = algebraic_sum(&values);
    println!("result = {result}");
}

使用这些方法时要接受两个边界条件:结果可能因编译器选择的优化而变化,不能依赖某一种固定的求值顺序;但这些方法不会因此引入未定义行为。它们更适合对吞吐量敏感、允许一定浮点结果差异的场景,例如图形计算、信号处理和部分科学计算。

如果业务要求跨平台逐位一致的结果,或者测试依赖严格的浮点求值顺序,就不应盲目替换普通运算。升级后应使用真实数据集重新跑精度和性能基准。

整数格式化:减少动态分发和中间开销

所有原生整数类型现在都有 format_into 方法,可以把十进制表示写入 core::fmt::NumBuffer<Self>。缓冲区足够容纳对应整数类型的任意十进制值,返回的字符串借用该缓冲区。

典型使用方式如下:

use core::fmt::NumBuffer;

fn format_id(id: u64) -> &str {
    let mut buffer = NumBuffer::<u64>::new();
    id.format_into(&mut buffer)
}

fn main() {
    let mut buffer = NumBuffer::<u64>::new();
    let text = 42_u64.format_into(&mut buffer);
    println!("id={text}");
}

NumBuffer 的具体构造接口和可用范围应以项目所使用的 1.98.0 标准库文档及编译器提示为准;上面的代码展示的是 API 的使用方向,迁移时应在目标工具链上编译确认。

这个 API 对高频整数格式化尤其有价值,例如协议编码、日志字段、指标导出和内存内序列化。它绕过了基于 write! 的部分动态分发,基准结果接近专用 itoa 实现,因此可以考虑把它纳入依赖清理评估。不过,是否真的更快仍取决于缓冲区生命周期、调用频率和整体 I/O 成本,不能只根据单次微基准下结论。

ManuallyDrop<Box<T>> 的保证更加清晰

Rust 1.96.0 修复了一个与 ManuallyDropBox 相关的编译器行为问题。Rust 1.98.0 进一步更新了文档,明确保证:释放其中的 Box 后,移动对应的 ManuallyDrop<Box<T>> 不会因为这个移动动作本身而构成未定义行为。

这并不意味着可以随意操作已经释放的资源,也不意味着 unsafe 代码自动变得安全。释放后的值仍然不能被当作有效的 Box 解引用或再次释放。改动的重点是明确移动容器本身的语义,减少对底层类型状态的错误推断。

其他稳定 API 值得顺手检查

  1. str::substr_range[T]::subslice_rangestrip_circumfix 简化了切片范围与前后缀处理。
  2. String::from_utf16lefrom_utf16be 及其 lossy 版本补充了明确端序的 UTF-16 转换能力。
  3. Atomic<T>::from_mutget_mut_slicefrom_mut_slice 改善了原子类型与可变引用之间的转换场景。
  4. NonZero 整数新增 from_str_radix
  5. std::process::CommandArgs 获得 Send/Sync 支持,便于在满足其他约束时跨线程传递。
  6. Cargo、Clippy 以及标准库还有其他变更,应结合完整发布说明检查是否影响项目 lint、构建和测试流程。

升级建议

Rust 1.98.0 适合按以下顺序采用:

  • 在 CI 中增加 1.98.0 编译和测试任务。
  • 为数值密集型代码增加普通浮点运算与 algebraic_* 方法的精度、吞吐量对比。
  • 为整数格式化场景测量分配次数、CPU 时间和端到端 I/O 时间。
  • 审查涉及 ManuallyDropBox 和其他 unsafe 容器操作的代码,按照新的文档保证更新注释和测试。
  • 确认 MSRV、交叉编译目标以及依赖包的兼容性,再更新团队默认工具链。

这次版本升级的价值主要集中在底层性能和 API 完整性。它不会自动让所有程序变快,但能为明确知道性能瓶颈在哪里的项目提供更直接的标准库工具。


相关推荐