在没有恐慌的情况下处理整数除法/余数溢出的最快方法是什么?

时间:2016-07-03 14:03:56

标签: performance rust

我仍在改进overflower以处理整数溢出。一个目标是能够使用#[overflow(wrap)]来避免溢出时的恐慌。但是,我发现标准整数类型的.wrapping_div(_).wrapping_rem(_)函数在除以零时实际上是恐慌。编辑:更好地激发这个用例:在中断处理程序中,我们绝对想避免恐慌。我假设div-by-zero条件极不可能,但我们仍然需要为某些有效定义返回“有效”值。

一种可能的解决方案是使值饱和(我在使用#[overflow(saturate)]注释代码时执行此操作),但这可能相对较慢(特别是因为其他更多操作也已饱和)。所以我想添加一个#[overflow(no_panic)]模式,完全避免恐慌,在所有情况下几乎和#[overflow(wrap)]一样快。

我的问题是:什么是返回某事(不关心什么)的最快方法,而不会因为将(除了余数)除以零而感到恐慌?

2 个答案:

答案 0 :(得分:2)

免责声明:这不是一个非常严肃的答案。它几乎肯定比使用if语句检查除数是否为零的天真解决方案要慢。

#![feature(asm)]

fn main() {
    println!("18 / 3 = {}", f(18, 3));
    println!("2555 / 10 = {}", f(2555, 10));
    println!("-16 / 3 = {}", f(-16, 3));
    println!("7784388 / 0 = {}", f(7784388, 0));
}

fn f(x: i32, y: i32) -> i32 {
    let z: i32;
    unsafe {
        asm!(
            "
            test %ecx, %ecx
            lahf
            and $$0x4000, %eax
            or %eax, %ecx
            mov %ebx, %eax
            cdq
            idiv %ecx
            "
            : "={eax}"(z)
            : "{ebx}"(x), "{ecx}"(y)
            : "{edx}"
        );
    }
    z
}

Rust Playground

答案 1 :(得分:1)

pub fn nopanic_signed_div(x: i32, y: i32) -> i32 {
    if y == 0 || y == -1 {
        // Divide by -1 is equivalent to neg; we don't care what
        // divide by zero returns.
        x.wrapping_neg()
    } else {
        // (You can replace this with unchecked_div to make it more
        // obvious this will never panic.)
        x / y
    }
}

这在x86-64上生成以下内容,其中“rustc 1.11.0-nightly(6e00b5556 2016-05-29)”:

    movl    %edi, %eax
    leal    1(%rsi), %ecx
    cmpl    $1, %ecx
    ja  .LBB0_2
    negl    %eax
    retq
.LBB0_2:
    cltd
    idivl   %esi
    retq

它应该在其他平台上产生类似的东西。

至少需要一个分支,因为LLVM IR认为除以零是未定义的行为。分别检查0和-1将涉及额外的分支。有了这些限制,实际上没有其他选择。

(使用内联汇编可能会稍微提高一些东西,但这会是一个糟糕的主意,因为在除以常量的情况下,最终会生成更糟糕的代码。)

这个解决方案是否真的合适可能取决于你的目标是什么;除以零可能是一个逻辑错误,因此默默地接受它似乎是一个坏主意。