2016-07-03 8 views
0

私はまだ整数オーバーフローを処理するためにoverflowerを改善しています。 1つ目の目標は、オーバーフロー時のパニックを避けるために#[overflow(wrap)]を使用できるようにすることでした。しかし、標準整数型の.wrapping_div(_).wrapping_rem(_)の関数は、ゼロで割ると実際はパニックに陥ることが分かりました。編集:このユースケースをより良く動機づけるには:割り込みハンドラ内で、パニックを避けることが絶対に必要です。私はdiv-by-zero条件は非常にありそうもないと仮定しますが、有効な定義については "有効な"値を返す必要があります。整数除算/剰余について、パニックを起こさずにオーバーフローを処理する最速の方法は何ですか?

(コードに#[overflow(saturate)]という注釈が付けられているときに行う)値を飽和させる可能性がありますが、これは比較的遅くなる可能性があります(特に他の操作も飽和しているため)。ですから、私は#[overflow(no_panic)]モードを追加して、パニックを完全に回避し、すべてのケースでほぼ同じ速さで#[overflow(wrap)]になります。

私の質問は以下の通りです。何かを返す最速の方法は何ですか(何も気にしないでください)。

+0

*何か(何を気にしない)*を返すための最速の方法は何ですか - プログラムの正しさが問題にならない場合、私はすべての部門を変換することをお勧めしたい/に余り引数にかかわらず、定数値 '42'になります。これにより、ほとんどのプログラムの速度が向上します。 – Shepmaster

+1

私はこの質問が全く特有のものではないと思う。私はおそらくそれを言い換えると(無関係なものをすべて削除する)、別のタグを選んだ。それはx86とアセンブリの人々によって最高に答えられるかもしれないようです... –

+0

いくつかのケースでは、0は42より速いかもしれません( 'xor eax、eax' vs 'mov eax、42')。 Ditto Lukas。 – dtolnay

答えて

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 
    } 
} 

これは、 "rustc 1.11.0-毎晩(6e00b5556 2016年5月29日)" とx86-64の上、以下を生成します。これは、他のプラットフォームで似たようなを生成する必要があり

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

少なくとも1つのブランチが必要です。これは、LLVM IRがゼロで除算することが未定義の動作であるとみなすためです。 0と-1を別々にチェックするには、別のブランチが必要です。これらの制約によって、実際には他の選択肢はありません。

(インラインアセンブリとわずかに速く何かを思い付くすることは可能かもしれないが、あなたは定数で割った場合にははるかに悪いコードを生成終わるだろうので、それはひどいアイデアだろう。)


実際にこのソリューションが適切かどうかは、目標が何であるかによって異なります。ゼロによる除算はおそらくロジックエラーですので、それを静かに受け入れることは悪い考えです。

+1

アセンブリや、さまざまなコンパイラのバージョン、プラットフォームなどを含めることもできます。これらのすべてが "最速"の評価に役立ちます。たとえば、PPCアセンブリの外観はどうですか?またはARMですか? – Shepmaster

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

+0

このソリューションがサポートするさまざまなコンパイラのバージョン、プラットフォームなどを含めることもできます。これらのすべてが「最速」の評価に貢献します。たとえば、これはRustがサポートするすべてのティア1プラットフォームではうまくいかないでしょう。 – Shepmaster

+2

@Shepmasterええ、これはあまり移植性がありません。夜間のコンパイラが必要です。ただし、すべてのティア1プラットフォームを含むすべてのx86システムで動作するはずです。 – Adrian

関連する問題