乗算または除算を使用する必要がありますか?


118

これはばかげた楽しい質問です:

変数の値の半分が必要な単純な操作を実行する必要があるとしましょう。通常、これに 2つの方法があります。

y = x / 2.0;
// or...
y = x * 0.5;

言語で提供されている標準の演算子を使用していると仮定すると、どちらの方がパフォーマンスが優れていますか?

乗算は通常より良いと思いますので、コードを書くときはそれを守るようにしますが、これを確認したいと思います。

個人的にはPython 2.4-2.5 の回答に興味がありますが、他の言語の回答も投稿してください。また、必要に応じて、他のより洗練された方法(ビット単位のシフト演算子の使用など)も投稿してください。


5
ベンチマークを実行しましたか?たった数十行のコードです。ベンチマークを実行して何を学びましたか?[ヒント:ここに質問を投稿するよりも、そうする方が早いでしょう。]
S.Lott 2008年

4
非常に興味深い回答/ディスカッションを生成した素晴らしい質問。ありがとう:)
stealthcopter

22
ベンチマークで答えを学んだとしても、それは有用な質問であり、いくつかの興味深い有用な答えを生み出しています。また、問題に最適化を行う価値があるかどうかに関係のないアドバイスを提供する回答やコメントへの回答を書くことを控えてほしいと思います。OPが「本当に」大規模な書き換えに関するアドバイスを望んでいると想定するのではなく、OPが書かれているとおりに質問していると想定しないでください。
ケビンホワイトフット2013年

1
除算は乗算よりはるかに遅いです。ただし、一部のスマートコンパイラ/ VMは除算を乗算に変換するため、テストは同じ結果になります(両方のテストで乗算がテストされます)。
Ivan Kuckir 14

4
少し外れたトピックですが、@ KevinWhitefootにどれだけ同意するかを言いたいだけです。技術的な質問に対する技術的な答えを強調するよりも、説教者から読むほどイライラすることはありません。あなたのコメントをケビンに感謝します!
ジャン=フランソワ・

回答:


78

Python:

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 / 2.0'
real    0m26.676s
user    0m25.154s
sys     0m0.076s

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 * 0.5'
real    0m17.932s
user    0m16.481s
sys     0m0.048s

乗算が33%高速

ルア:

time lua -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m7.956s
user    0m7.332s
sys     0m0.032s

time lua -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m7.997s
user    0m7.516s
sys     0m0.036s

=>実際の違いはありません

LuaJIT:

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m1.921s
user    0m1.668s
sys     0m0.004s

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m1.843s
user    0m1.676s
sys     0m0.000s

=>それはわずか5%高速です

結論:Pythonでは、除算よりも乗算の方が高速ですが、より高度なVMまたはJITを使用してCPUに近づくと、利点はなくなります。将来のPython VMがそれを無関係にする可能性はかなりあります


ベンチマークにtimeコマンドを使用する際のヒントをありがとう!
Edmundito、2008年

2
あなたの結論は間違っています。JIT / VMが向上するにつれて、関連性が高まります。分割は、VMのオーバーヘッドが低い場合に比べて遅くなります。コンパイラは通常、精度を保証するために浮動小数点をあまり最適化できないことに注意してください。
rasmus 2012

7
@rasmus:JITが良くなると、除算を要求してもCPU乗算命令を使用する可能性が高くなります。
Ben Voigt 2013

68

常に最も明確なものを使用してください。他にあなたがすることは、コンパイラーをしのぐことです。コンパイラーがまったくインテリジェントである場合、結果を最適化するために最善を尽くしますが、次の人があなたの安っぽいビットシフトソリューションを嫌うことはありません(ちなみに私はビット操作が大好きですが、楽しいです。 )

時期尚早の最適化は、すべての悪の根源です。常に3つの最適化ルールを覚えておいてください。

  1. 最適化しないでください。
  2. あなたが専門家なら、ルール#1を見てください
  3. あなたが専門家であり、必要性を正当化できる場合は、次の手順を使用します。

    • 最適化されていないコード
    • 「十分に速い」速度を決定します。どのユーザー要件/ストーリーがそのメトリックを必要とするかに注意してください。
    • 速度テストを書く
    • 既存のコードをテストします-十分に高速であれば、完了です。
    • 最適化して再コーディング
    • 最適化されたコードをテストします。指標を満たしていない場合は、破棄して元のままにします。
    • テストに合格した場合は、元のコードをコメントとして保持します

また、不要な場合に内部ループを削除したり、挿入ソート用の配列よりリンクリストを選択したりすることは最適化ではなく、プログラミングだけです。


7
それは完全なKnuthの引用ではありません。en.wikipedia.org/wiki/…を
Jason S

いいえ、同じように多くの異なるソースからの主題に関する約40の異なる引用があります。私は一緒にいくつかの作品のようなものです。
ビルK

最後の文では、ルール#1と#2をいつ適用するかが不明確になっているため、最初に戻ったところです。価値のある最適化とそうでない最適化を決定する必要があります。答えが明白なふりは答えではありません。
Matt

2
それは本当にあなたを混乱させるのですか?実際にクライアントの仕様を満たしていない場合や、CPUの言語やキャッシング特性を含むシステム全体に精通している場合を除き、常にルール1と2を適用してください。その時点で、3の手順のみに従うようにしてください。「ねえ、ゲッターを呼び出す代わりにこの変数をローカルにキャッシュすると、おそらく速くなるでしょう。まず、十分に速くないことを証明してから、各最適化を個別にテストし、役に立たないものは捨ててください。途中で大量のドキュメントを作成してください
ビルK

49

これは非常に厄介なものになっているので、コードを読みやすくするために何をしてもよいでしょう。何千回とは言わないまでも何千回も操作を実行しない限り、違いに気付く人はいないでしょう。

本当に選択する必要がある場合は、ベンチマークが唯一の方法です。問題を引き起こしている関数を見つけ、関数のどこで問題が発生しているかを調べ、それらのセクションを修正します。ただし、単一の数学演算(1回でも何度も繰り返される)がボトルネックの原因になるかどうかは、まだ疑問です。


1
私がレーダープロセッサを作成していたとき、1つの操作で違いがありました。しかし、マシンコードを手動で最適化して、リアルタイムのパフォーマンスを実現しました。それ以外については、私は単純明快に投票します。
S.Lott、2008年

たぶん、あなたは単一の操作を気にするかもしれません。しかし、私はそこにあるアプリケーションの99%でそれは問題ではないと予想します。
Thomas Owens、

27
特にOPはPythonで答えを探していたので。その量の効率を必要とするものはPythonで書かれるとは思いません。
Ed S.

4
除算は、三角形の交差ルーチンで最も費用のかかる操作であり、ほとんどのレイトレーサーの基本です。除算の代わりに逆数と乗算を保存すると、何倍もの高速化が発生します。
2010年

@solinent-はい、スピードアップですが、 "何度も"疑問です-浮動小数点除算と乗算は、除算ではなく、乗算用に本当に最適化されていない限り、約4:1を超えてはいけません。
Jason S

39

乗算はより速く、除算はより正確です。数値が2のべき乗でない場合は、精度がいくらか失われます。

y = x / 3.0;
y = x * 0.333333;  // how many 3's should there be, and how will the compiler round?

コンパイラーに完全な精度で逆定数を計算させても、答えは異なる場合があります。

x = 100.0;
x / 3.0 == x * (1.0/3.0)  // is false in the test I just performed

速度の問題は、C / C ++またはJIT言語でのみ問題になる可能性が高く、その場合でも、操作がボトルネックのループにある場合に限ります。


整数で除算する場合、除算は正確です。
台座、

7
分母>分子による浮動小数点除算では、下位ビットに意味のない値を導入する必要があります。通常、分割すると精度が低下します。
S.Lott、2008年

8
@ S.Lott:いいえ、そうではありません。すべてのIEEE-754準拠の浮動小数点実装は、現在の丸めモードに関して、すべての演算の結果を完全に(つまり、最も近い浮動小数点数に)丸める必要があります。少なくとも1つの丸めが発生する必要があるため、逆数を掛けると常にエラーが増えます。
エレクトロ

1
この答えは8年以上前のものですが、誤解を招く可能性があります。精度を大幅に低下させることなく除算を実行できます。y = x * (1.0/3.0);コンパイラは通常、コンパイル時に1/3を計算します。はい、IEEE-754では1/3は完全には表現できませんが、浮動小数点演算を実行しているときは、下位ビットが丸められるため、乗算または除算を実行しているとにかく精度が失われます。計算が丸め誤差の影響を受けやすいことがわかっている場合は、問題に最もよく対処する方法も知っている必要があります。
Jason S

1
@JasonS私は、1.0から始まり、1 ULPまでカウントアップして、プログラムを一晩実行したままにしました。で乗算した結果をで除算した結果と比較し(1.0/3.0)ました3.0。私は1.0000036666774155に達しました、そしてそのスペースで結果の7.3%が異なっていました。私はそれらが1ビットだけ異なっていたと思いますが、IEEE演算は最も近い正しい結果に丸めることが保証されているので、除算がより正確であるという私の声明を支持します。違いが重要かどうかはあなた次第です。
Mark Ransom

25

コードを最適化したいが、それでも明確にしたい場合は、これを試してください:

y = x * (1.0 / 2.0);

コンパイラーはコンパイル時に除算を実行できる必要があるため、実行時に乗算が行われます。私は精度が同じであると期待しますy = x / 2.0ケースます。

これが問題になる可能性があるのは、浮動小数点演算を計算するために浮動小数点エミュレーションが必要な組み込みプロセッサです。


12
自分自身(そしてこれを-1にした人)に合ってください。これは組み込みの世界では標準的な慣行であり、その分野のソフトウェアエンジニアはそれを明らかにしています。
Jason S

4
+1は、コンパイラが浮動小数点演算を最適化できないことをここで唯一認識しているためです。精度を保証するために、乗算のオペランドの順序を変更することもできません(緩和モードを使用する場合を除く)。
rasmus 2012

1
OMG、初等数学が不明確であると考えているプログラマーは少なくとも6人います。AFAIK、IEEE 754乗算は可換です(ただし、関連付けはありません)。
maaartinus 2014

13
おそらくあなたは要点を見逃している。それは代数的正確性とは何の関係もありません。理想的な世界では、2でy = x / 2.0;割り切れるだけです。しかし、現実の世界では、コンパイラーを低コストで乗算する必要があります。なぜそれy = x * (1.0 / 2.0);がより良いのかは明確ではy = x * 0.5;なく、代わりに述べる方がより明確になるでしょう。しかし、変更2.07.0、私はむしろ参照してくださいねy = x * (1.0 / 7.0);よりy = x * 0.142857142857;
Jason S

3
これにより、メソッドを使用するほうが読みやすく(正確に)なる理由が明らかになります。
フアンマルティネス

21

「他の言語」オプションに何かを追加するだけです。
C:これは単なる学術的な演習なので、実際には何の違いもないので、何か別のことに貢献したいと思いました。

最適化なしでアセンブリにコンパイルし、結果を確認しました。
コード:

int main() {

    volatile int a;
    volatile int b;

    asm("## 5/2\n");
    a = 5;
    a = a / 2;

    asm("## 5*0.5");
    b = 5;
    b = b * 0.5;

    asm("## done");

    return a + b;

}

でコンパイル gcc tdiv.c -O1 -o tdiv.s -S

2による除算

movl    $5, -4(%ebp)
movl    -4(%ebp), %eax
movl    %eax, %edx
shrl    $31, %edx
addl    %edx, %eax
sarl    %eax
movl    %eax, -4(%ebp)

そして0.5による乗算:

movl    $5, -8(%ebp)
movl    -8(%ebp), %eax
pushl   %eax
fildl   (%esp)
leal    4(%esp), %esp
fmuls   LC0
fnstcw  -10(%ebp)
movzwl  -10(%ebp), %eax
orw $3072, %ax
movw    %ax, -12(%ebp)
fldcw   -12(%ebp)
fistpl  -16(%ebp)
fldcw   -10(%ebp)
movl    -16(%ebp), %eax
movl    %eax, -8(%ebp)

ただし、これらintのsをdoubles に変更すると(これはpythonがおそらく行うことです)、次のようになりました。

分割:

flds    LC0
fstl    -8(%ebp)
fldl    -8(%ebp)
flds    LC1
fmul    %st, %st(1)
fxch    %st(1)
fstpl   -8(%ebp)
fxch    %st(1)

乗算:

fstpl   -16(%ebp)
fldl    -16(%ebp)
fmulp   %st, %st(1)
fstpl   -16(%ebp)

私はこのコードをベンチマークしていませんが、コードを調べるだけで、整数を使用すると、2による除算は2による乗算よりも短いことがわかります。同じ操作にそれらを使用しないよりもおそらく高速に実行されます(実際にはわかりません)。最終的にこの答えは、0.5による乗算と2による除算の乗算のパフォーマンスが、言語の実装とそれが実行されるプラットフォームに依存することを示しています。結局のところ、違いはごくわずかであり、読みやすさの点を除いて、ほとんど気にする必要のないものです。

余談ですが、私のプログラムでmain()はが返されますa + b。volatileキーワードを削除すると、アセンブリがどのように表示されるか(プログラムのセットアップを除く)を推測することはできません。

## 5/2

## 5*0.5
## done

movl    $5, %eax
leave
ret

除算、乗算、および加算の両方を1つの命令で実行しました。オプティマイザがなんらかの立派なものであれば、明らかにこれを心配する必要はありません。

答えが長すぎてすみません。


1
「単一の指示」ではありません。それは、常に折りたたまれています。
kvanberendonck 2013

5
@kvanberendonckもちろん、それは単一の命令です。それらを数える:movl $5, %eax 最適化の名前は重要ではなく、関連性さえありません。あなたはちょうど4年前の答えに屈することを望んでいました。
カーソンマイヤーズ

2
最適化の性質は、状況依存であるため、理解することは依然として重要です。これは、追加/乗算/除算などを行う場合にのみ適用されます。コンパイル時の定数。コンパイラは事前にすべての計算を実行し、実行時に最終的な回答をレジスタに移動します。除算は一般的な場合の乗算(実行時除数)よりもはるかに遅くなりますが、逆数による乗算は、とにかく同じ分母で2回以上除算する場合にのみ役立つと思います。あなたはおそらくそれをすべて知っているでしょうが、新しいプログラマーはそれを綴る必要があるかもしれないので...念のために。
Mike S

10

まず、CまたはASSEMBLYで作業しているのでない限り、おそらくメモリストールと一般的な呼び出しオーバーヘッドにより、乗算と除算の違いが無関係な点にまで達しない、より高レベルの言語を使用していることでしょう。その場合は、その場合に読みやすいものを選択してください。

非常に高いレベルから話している場合、それを使用する可能性が高いものについては、測定可能なほど遅くなることはありません。他の答えでわかるように、2つのミリ秒未満の差を測定するためだけに、100万回の乗算/除算を実行する必要があります。

それでも興味がある場合は、低レベルの最適化の観点から:

除算は、乗算よりもパイプラインが大幅に長くなる傾向があります。つまり、結果を取得するのに時間がかかりますが、依存しないタスクでプロセッサをビジー状態に保つことができれば、乗算よりもコストがかかることにはなりません。

パイプラインの違いがどのくらい続くかは、完全にハードウェアに依存します。最後に使用したハードウェアは、FPU乗算では9サイクル、FPU除算では50サイクルのようなものでした。たくさん聞こえますが、メモリミスのために1000サイクルを失うので、物事を展望することができます。

アナロジーは、テレビ番組を見ながらパイを電子レンジに入れることです。テレビ番組から離れた合計時間は、電子レンジに入れて電子レンジから取り出すまでの時間です。残りの時間は、まだテレビ番組を見ていました。したがって、パイが1分ではなく10分で調理された場合、テレビの視聴時間を実際に使い果たすことはありませんでした。

実際には、乗算と除算の違いを気にするレベルに到達する場合は、パイプライン、キャッシュ、ブランチストール、順不同予測、パイプラインの依存関係を理解する必要があります。これがこの質問に行くつもりであったように聞こえない場合、正しい答えは2つの間の違いを無視することです。

何年も前に、除算を回避して常に乗算を使用することが絶対的に重要でしたが、当時はメモリヒットの関連性は低く、除算ははるかに悪かったです。最近では、読みやすさを高く評価していますが、読みやすさの違いがない場合は、乗算を選択することをお勧めします。


7

あなたの意図をより明確に述べている方を書いてください。

プログラムが機能したら、何が遅いのかを理解し、それを速くします。

逆にしないでください。


6

必要なことは何でもしてください。最初に読者のことを考えてください。パフォーマンスの問題があると確信するまで、パフォーマンスについて心配する必要はありません。

コンパイラにパフォーマンスを任せましょう。


5

整数または非浮動小数点型を使用している場合は、ビットシフト演算子を忘れないでください:<< >>

    int y = 10;
    y = y >> 1;
    Console.WriteLine("value halved: " + y);
    y = y << 1;
    Console.WriteLine("now value doubled: " + y);

7
この最適化は、最新のコンパイラーの背後で自動的に実行されます。
ダスティンゲッツ

オペランド(?)に代わりにそれを使用するシフト可能なバージョンがあるかどうかを(ビット演算を使用して)チェックするかどうか誰かがテストしましたか?function mul(a、b){if(b is 2)return a << 1; (bが4)の場合、a << 2を返します。// ...などはa * b;を返します }私の推測では、IFは非常に高価であるため、効率が低下するでしょう。
クリストファーライトフット

それは私が想像したものに近いところには印刷されませんでした。気にしないで。
クリストファーライトフット

const操作の場合、通常のコンパイラーが処理を行う必要があります。しかし、ここではpythonを使用しているので、それを知るのに十分スマートかどうかはわかりません。(そのはず)。
クリストファーライトフット

実際に何が起こっているのかすぐに明確にならないことを除いて、良いショートカットです。ほとんどのプログラマーはビットシフト演算子さえ認識していません。
Blazemonger、2011

4

実際には、一般的な経験則として、乗算は除算よりも速いという正当な理由があります。ハードウェアでの浮動小数点除算は、シフトおよび条件付き減算アルゴリズム(2進数を使用した「長除算」)または-最近の可能性が高い-Goldschmidtのアルゴリズムのような反復で行われます。シフトと減算には、ビットの精度ごとに少なくとも1サイクルが必要です(反復は、乗算のシフトと加算と同様に並列化がほぼ不可能です)。反復アルゴリズムは、反復ごとに少なくとも1つの乗算を行います。。どちらの場合も、除算にさらにサイクルがかかる可能性が高くなります。もちろん、これはコンパイラーの癖、データ移動、または精度を考慮していません。することにより、大規模、しかし、あなたは、プログラムの時間に敏感な部分で内側のループをコーディング書いている場合0.5 * xか、1.0/2.0 * xというよりは、x / 2.0行うには合理的なものです。「最も明確なものをコード化する」という教訓は完全に真実ですが、これら3つすべては非常に読みやすいため、この場合の教訓は単なる教訓的です。


3

私は乗算がより効率的であることを常に学びました。


「効率的」は間違った言葉です。ほとんどのプロセッサは、割るよりも速く増殖します。ただし、最新のパイプライン化されたアーキテクチャでは、プログラムに違いが見られない場合があります。他の多くの人が言っているように、人間にとって最も読みやすいものを実行することで、あなたは本当にはるかに良くなります。
TED

3

乗算は通常高速です-確かに遅くなることはありません。ただし、速度が重要でない場合は、どちらか明確な方を記述してください。


2

浮動小数点の除算は(一般に)特に遅いため、浮動小数点の乗算も比較的低速ですが、おそらく浮動小数点の除算よりも高速です。

しかし、プロファイリングで除算が乗算と比較してボトルネックになっていることが示されていない限り、私は「実際には問題ではない」と答える傾向があります。ただし、乗算と除算の選択がアプリケーションのパフォーマンスに大きな影響を与えることはないと思います。


2

これは、アセンブリまたはおそらくCでプログラミングしているときにさらに問題になります。現代のほとんどの言語では、このような最適化が行われていると思います。


2

「通常、乗算のほうが良いと思うので、コードを書くときはそれを守るようにします。」

この特定の質問に関連して、ここでより良いとは「より速い」という意味です。これはあまり役に立ちません。

速度について考えることは重大な間違いです。計算の特定の代数形式には、重大なエラーの影響があります。

エラー分析を伴う浮動小数点演算を参照してください。浮動小数点演算とエラー分析の基本的な問題を参照してください。

一部の浮動小数点値は正確ですが、ほとんどの浮動小数点値は概算です。これらは、いくつかの理想的な値といくつかの誤差です。すべての操作は、理想値とエラー値に適用されます。

最大の問題は、ほぼ等しい2つの数値を操作しようとすることから生じます。右端のビット(エラービット)が結果を支配するようになります。

>>> for i in range(7):
...     a=1/(10.0**i)
...     b=(1/10.0)**i
...     print i, a, b, a-b
... 
0 1.0 1.0 0.0
1 0.1 0.1 0.0
2 0.01 0.01 -1.73472347598e-18
3 0.001 0.001 -2.16840434497e-19
4 0.0001 0.0001 -1.35525271561e-20
5 1e-05 1e-05 -1.69406589451e-21
6 1e-06 1e-06 -4.23516473627e-22

この例では、値が小さくなるにつれ、ほぼ等しい数の差により、正解がゼロであるゼロ以外の結果が作成されることがわかります。


1

C / C ++では乗算がより効率的であるとどこかで読んだことがあります。インタプリタ言語に関するアイデアはありません-他のすべてのオーバーヘッドのため、違いはおそらく無視できます。

それがより保守可能/読みやすいものに固執する問題にならない限り-私は人々がこれを言ったときにそれが嫌いですが、それは真実です。


1

除数が0でないことを確認するためにサイクルを費やす必要がないため、一般的に乗算をお勧めします。除数が定数の場合、これは適用されません。


1

Samsung Android GT-S5830でプロファイルされたJava Android

public void Mutiplication()
{
    float a = 1.0f;

    for(int i=0; i<1000000; i++)
    {
        a *= 0.5f;
    }
}
public void Division()
{
    float a = 1.0f;

    for(int i=0; i<1000000; i++)
    {
        a /= 2.0f;
    }
}

結果?

Multiplications():   time/call: 1524.375 ms
Division():          time/call: 1220.003 ms

除算は乗算よりも約20%高速です(!)


1
現実的にするにはa = i*0.5、ではなく、テストする必要がありa *= 0.5ます。これが、ほとんどのプログラマーが操作を使用する方法です。
Blazemonger、2011

1

投稿#24(乗算が速い)と#30の場合と同じですが、両方とも理解しやすい場合もあります。

1*1e-6F;

1/1e6F;

〜どちらも読みやすく、何十億回も繰り返さなければなりません。したがって、通常は乗算が高速であることを知っておくと便利です。


1

違いはありますが、コンパイラによって異なります。最初はvs2003(c ++)で、double型(64ビット浮動小数点)に大きな違いはありませんでした。ただし、vs2010でテストを再度実行すると、乗算の場合は最大で4倍速くなる大きな違いが検出されました。これを追跡すると、vs2003とvs2010は異なるfpuコードを生成するようです。

Pentium 4、2.8 GHz、vs2003:

  • 乗算:8.09
  • 除算:7.97

Xeon W3530、vs2003の場合:

  • 乗算:4.68
  • 除算:4.64

Xeon W3530、vs2010:

  • 乗算:5.33
  • 部門:21.05

vs2003では、ループ内の除算(除数が複数回使用された)が逆の乗算に変換されたようです。vs2010では、この最適化は適用されなくなりました(2つの方法の結果がわずかに異なるためだと思います)。また、分子が0.0になるとすぐに、cpuはより速く除算を実行することに注意してください。チップに組み込まれている正確なアルゴリズムはわかりませんが、おそらく数に依存します。

2013年3月18日編集:vs2010の観察


コンパイラーが、たとえばn/10.0、次の形式の式で置き換えることができなかった理由があるのだろうか(n * c1 + n * c2)?ほとんどのプロセッサでは、除算には2つの乗算と1つの除算よりも時間がかかると予想されます。また、定数による除算は、示された定式化を使用するすべてのケースで正しく丸められた結果を生成できると思います。
スーパーキャット2014年

1

これはばかげた楽しい答えです:

x / 2.0x * 0.5と同等ではありません

このメソッドを2008年10月22日に作成したとします。

double half(double x) => x / 2.0;

さて、10年後、このコードを最適化できることがわかります。このメソッドは、アプリケーション全体で数百の数式で参照されています。したがって、それを変更すると、パフォーマンスが5%向上します。

double half(double x) => x * 0.5;

コードを変更するのは正しい決定でしたか?数学では、2つの式は実際に同等です。コンピュータサイエンスでは、それが常に当てはまるとは限りません。お読みください精度の問題の影響を最小限に抑える詳細については。計算された値が-ある時点で-他の値と比較される場合、エッジケースの結果を変更します。例えば:

double quantize(double x)
{
    if (half(x) > threshold))
        return 1;
    else
        return -1;
}

結論は; 2つのうちどちらかに落ち着いたら、それに固執します。


1
反対票?あなたの考えを説明するコメントはどうですか?この答えは間違いなく100%関連しています。
l33t 2018年

コンピュータサイエンスでは、浮動小数点値の2のべき乗による乗算/除算は、値が非正規化またはオーバーフローにならない限り、ロスレスです。
2018

浮動小数点は除算時にロスレスではないので、ステートメントが正しいかどうかは問題ではありません。もしそうなら、私は非常に驚きます。
l33t

1
「浮動小数点は、除算時にロスレスではありません」とは、廃止されたx87コードを出力する古代のコンパイラでビルドしている場合に限られます。:最新のハードウェア上でだけフロート/ダブル変数は、32または64ビットIEEE 754、無損失で有するen.wikipedia.org/wiki/IEEE_754あなたが0.5によって2または乗算で割る場合、方法のためIEEE 754作品のあなたが減少1の指数。残りのビット(符号+仮数)は変化しません。そして、両方20.5数字は(例えばとは異なり、精度を損なうことなく、正確にIEEE 754で表すことができる0.4か、0.1彼らは、することはできません)。
2018

0

さて、追加/サブトラック操作のコストが1であると仮定すると、乗算のコストは5で、除算のコストは約20です。


これらの数字はどこから入手したのですか?経験?直感?インターネット上の記事?それらは異なるデータ型に対してどのように変化しますか?
kroiz 2014

0

このような長く興味深い議論の後、ここで私の見解を述べます。この質問に対する最終的な回答はありません。一部の人々がそれがハードウェア(piotrkgast128を参照)とコンパイラー(@Javierのテストを参照)の両方に依存すると指摘したように。速度が重要でない場合、アプリケーションがリアルタイムで大量のデータを処理する必要がない場合は、除算を使用して明快さを選択できますが、処理速度またはプロセッサの負荷が問題である場合は、乗算が最も安全です。最後に、アプリケーションがデプロイされるプラットフォームを正確に知らない限り、ベンチマークは意味がありません。そして、コードを明確にするために、1つのコメントで十分です。


-3

技術的には除算などはなく、逆元による乗算のみです。たとえば、2で割ることはありません。実際には0.5を掛けます。

「課」 -それは第二のために存在していることをみましょう子供自身は- 「デバイド」にあるため、常に難しいものの乗算であるxことにより、y一つの第一のニーズ値を計算するy^{-1}ようにy*y^{-1} = 1して、乗算を行いますx*y^{-1}。あなたがすでに知っているならy^{-1}、それから計算しyないことは最適化でなければなりません。


3
これは、シリコンに存在する両方のコマンドの現実を完全に無視します。
NPSF3000 2012

@ NPSF3000-フォローしていません。両方の演算が存在するという仮定の下で、それは単に除算演算が乗法的逆数と乗算の計算を暗黙的に含むことを主張し、これは単一の乗算を実行するよりも常に困難になります。シリコンは実装の詳細です。
satnhak

@ BTyler。両方のコマンドがシリコンに存在し、どちらのコマンドも(予想どおり)同じサイクル数かかる場合、命令がどれほど複雑であるかは、パフォーマンスPOVとはまったく関係ありません。
NPSF3000 2012

@ NPSF3000-ただし、乗算の方が速いため、両方が同じサイクル数を取るわけではありません。
satnhak
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.