我可以在每次除法时禁用检查零除法吗?

Can I disable checking for zero division every time the division happens?

为了更好的理解Rustspanic/exception机制,我写了如下一段代码:

#![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 在编译时知道某些东西为零时不会做任何事情 "smart",因此用户输入。只需给它一个 'A' 就可以了。

我发现 Rust 实际上生成的代码每次在除法发生之前都会检查零除法。我什至看了一次大会。 :-)

长话短说:我可以禁用此行为吗?我想对于更大的数据集,这会对性能产生相当大的影响。为什么不使用我们的 CPU 能力来为我们检测这些东西呢?我可以设置自己的信号处理程序并改为处理 SIGFPE 吗?

an issue on Github来看,前段时间情况肯定有所不同。

我认为事先检查每个分区与 "zero-cost" 相去甚远。你怎么看?我是否漏掉了一些明显的东西?

I think checking every division beforehand is far away from "zero-cost". What do you think?

你测量了什么?

执行的指令数是性能非常差的代表;矢量化代码通常更冗长,但速度更快。

所以真正的问题是:这个分支的成本是多少?

由于故意除以 0 的可能性很小,而无意中除以 0 的可能性稍大一些,因此分支总是会被正确预测 除以 0 时除外。但是,考虑到恐慌的代价,错误预测的分支是你最不担心的。

因此,成本是:

  • 稍微胖一点的组合,
  • 分支预测器中的一个占用槽。

确切的影响很难确定,对于数学密集型代码,它可能会产生影响。虽然我会提醒你整数除法是 ~100 周期 1 开始,所以数学重的代码会尽可能地避开它(它可能是最耗时的一个) CPU).

中的说明

1 参见 Agner Fog's Instruction Table:例如关于 Intel Nehalem DIV 和 IDIV 关于 64 位积分分别有 28 到 90 个周期和 37 到 100 个周期的延迟。


除此之外,rustc 是在 LLVM 之上实现的,它委托实际代码生成。因此,rustc 在很多情况下都受 LLVM 的支配,这就是其中之一。

LLVM有两条整数除法指令:udiv and sdiv.

两者都有除数为 0 的未定义行为。

Rust 旨在消除未定义行为,因此 防止除以 0 的发生,以免优化器破坏发出的代码而无法修复。

它按照 LLVM 手册中的建议使用检查。

Long story short: Can I disable this behaviour?

是的,你可以:std::intrinsics::unchecked_div(a, b)。 您的问题也适用于余数(这就是 Rust 调用 modulo): std::intrinsics::unchecked_rem(a, b) 的方式。 我检查了程序集输出 here 以将其与 C++ 进行比较。

在文档中指出:

This is a nightly-only experimental API. (core_intrinsics)

intrinsics are unlikely to ever be stabilized, instead they should be used through stabilized interfaces in the rest of the standard library

因此您必须使用每晚构建,并且由于 已经指出的原因,它不太可能以稳定的形式出现在标准库中。