为了更好地理解Rust的panic/异常机制,我写了下面一段代码:
#![feature(libc)]
extern crate libc;
fn main() {
let mut x: i32;
unsafe {
x = libc::getchar();
}
let y = x - 65;
println!("{}", x);
let z = 1 / y;
println!("{}", z);
}
我想检查 Rust 如何处理被零除的情况。最初我认为它要么是在面对一个未处理的 SIGFPE 并死亡,要么是它实现了一个处理程序并将其重新路由到一个恐慌(现在可以处理吗?)。
代码很冗长,因为我想确保 Rust 在编译时知道某些内容为零(因此用户输入为零)时不会做任何“智能”的事情。只需给它一个“A”,它就可以解决问题。
我发现 Rust 实际上生成的代码每次在除法发生之前都会检查零除法。我什至看了一次大会。 :-)
长话短说:我可以禁用此行为吗?我想对于更大的数据集,这可能会对性能产生相当大的影响。为什么不利用我们的 CPU 能力来为我们检测这些东西呢?我可以设置自己的信号处理程序并处理 SIGFPE 吗?
根据 Github 上的一个问题,前一段时间情况肯定有所不同。
我认为事先检查每个部门离“零成本”还很远。你怎么认为?我错过了一些明显的东西吗?
我认为事前检查每个部门离“零成本”还很远。你觉得怎么样?
你测量了什么?
执行的指令数量是性能的一个非常差的指标;矢量化代码通常更详细,但速度更快。
所以真正的问题是:这个分支机构的成本是多少?
由于故意除以 0 的可能性相当小,并且偶然除以 0 的可能性稍大一些,因此分支总是会被正确预测 除了 当发生除以 0 时。但是,考虑到恐慌的代价,错误预测的分支是你最不用担心的。
因此,成本为:
确切的影响很难确定,对于数学密集的代码来说它可能会产生影响。不过我要提醒您,整数除法一开始大约需要 100 个周期1,因此数学密集型代码会尽可能避免使用它(它可能是 CPU 中最耗时的指令)。
1 参见 Agner Fog 的指令表:例如,在 Intel Nehalem DIV 和 IDIV 上,64 位积分的延迟分别为 28 至 90 个周期和 37 至 100 个周期。
除此之外,rustc 是在 LLVM 之上实现的,它将实际的代码生成委托给 LLVM。因此,rustc 在很多情况下都受到 LLVM 的支配,这就是其中之一。
LLVM 有两个整数除法指令:udiv 和 sdiv。
两者都具有除数为 0 的未定义行为。
Rust 旨在消除未定义行为,因此 has 防止除数为 0 的情况发生,以免优化器破坏发出的代码而无法修复。
它使用 LLVM 手册中建议的检查。
长话短说:我可以禁用此行为吗?
std::intrinsics::unchecked_div(a, b)
。
你的问题也适用于余数(这就是 Rust 调用 modulo 的方式):std::intrinsics::unchecked_rem(a, b)
。
我检查了汇编输出here,将其与 C++ 进行比较。
在文档中指出:
这是一个仅限夜间的实验性 API。 (核心_内在)
内在函数不太可能稳定,相反,它们应该通过标准库其余部分中的稳定接口来使用
因此,您必须使用夜间构建,并且由于 Matthieu M. 已经指出的原因,它不太可能以稳定的形式进入标准库。
来自 Rust 社区 Discord 服务器上的 Cherry,另一种方法是使用分子类型的
NonZero
版本作为分母:
#[no_mangle]
pub fn panic_div(val: u32) {
_ = std::hint::black_box(5) / std::hint::black_box(val);
}
use std::num::NonZeroU32;
#[no_mangle]
pub fn no_panic(val: NonZeroU32) {
_ = std::hint::black_box(5) / std::hint::black_box(val);
}
产生:
panic_div:
push rax
mov dword ptr [rsp + 4], 5
lea rax, [rsp + 4]
mov dword ptr [rsp], edi
mov rax, rsp
cmp dword ptr [rsp], 0
je .LBB0_2
pop rax
ret
.LBB0_2:
lea rdi, [rip + .L__unnamed_1]
call qword ptr [rip + core::panicking::panic_const::panic_const_div_by_zero::h0158edae44a9fd47@GOTPCREL]
no_panic:
mov dword ptr [rsp - 8], 5
lea rax, [rsp - 8]
mov dword ptr [rsp - 4], edi
lea rax, [rsp - 4]
ret