2009-07-18 10 views
25

を掛け浮動対追加何、乗算や除算の使用を避け、代わりに加算と減算を使用する数値コードを書くことは価値があるでした。良い例は、多項式を直接計算するのではなく、多項式曲線を評価するのにforward differencesを使用することです。浮動小数点の相対速度は、ポイントが10年か20年前

はまだケースこれは、あるいは現代のコンピュータアーキテクチャは+、より遅い*、/もはやあるポイントに何回も進んでいますか - ?

具体的には、私は大規模なオンボード浮動小数点ハードウェアではなくソフトウェアでFPをやろうと小さなマイクロ付きのモダンな典型的なのx86チップ上で実行してコンパイルされたC/C++コードに興味があります。パイプライニングやその他のアーキテクチャーの強化により、特定のサイクル数が除外されていることがわかりましたが、私はまだ便利な直感を得たいと思います。

答えて

20

また、命令の組み合わせによっても異なります。あなたのプロセッサにはいつでも複数の計算ユニットが待機しており、すべてが常に満たされていれば最大のスループットを得ることができます。したがって、mulのループを実行するのは、ループや追加を実行するのと同じくらい速いですが、式が複雑になる場合も同じです。

たとえば、このループを取る:NUMITERため

for(int j=0;j<NUMITER;j++) { 
    for(int i=1;i<NUMEL;i++) { 
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; 
    } 
} 

= 10^7、関数numelは=^2 10、小さな正の数(NaNには、はるかに遅い)に初期化両方のアレイが、これは使用して6.0秒かかり64ビットのprocで2倍になります。私は

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ; 

とループを交換した場合にのみ、我々は追加の「overdid」以来、MULSは基本的に自由だったので... 1.7秒かかります。追加の削減が助けになりました。同じMULは/ディストリビューションを追加したが、今は定数に追加されたのではなくで乗算され、 - -

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; 

3.7秒をとります。それはのはもっと混乱するかもしれません。お使いのプロセッサは一般的な数値計算をより効率的に実行するように最適化されている可能性がありますだから、麦と秤の合計のようなドット積は、それが得られるほど良くなります。定数の追加はそれほど一般的ではないので、遅いです...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/ 

はもう一度1.7秒かかります。

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/ 

(初期ループと同じですが、高価な定数を追加しないでください:2。1秒)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/ 

(主にMULS、しかし、1つの追加:1.9秒)だから、基本的に

。どちらが速いのかは分かりませんが、ボトルネックを避けるためには、混乱を避け、NaNやINFを避け、定数を追加しないでください。どのようなことをしても、テストを確実に行い、さまざまなコンパイラ設定をテストすることができます。

いくつかのより多くの例:

bla *= someval; // someval very near 1.0; takes 2.1 seconds 
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds 
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds 
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86 
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86 
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86 
+1

命令のミックスは良い点です。私は200の浮動小数点DSPが600個の固定小数点DSPを実行しようとしていると主張している人がいます。ループ処理は絶対に必要なく、I/Oの処理に費やす時間は計算よりも長くなります。より速い固定小数点プロセッサーは全体的な命令ミックスに基づいて勝つだろうが、人々はちょうどFPユニットがデータ構造のハードウェア実装ではなく魔法であると考えている。 – NoMoreZealots

+0

ああ、魔法のappproach ;-) - それは残念です。 –

+1

直感的な例で素敵な説明! –

1

私は決定的なリファレンスは見つかりませんでしたが、最近の浮動小数点の乗算は、加減算とほぼ同じ速度ですが、除算はそうで​​はありません(ただし、何度も遅くはありません)。直感的には、自分の実験を実行するだけです。事前に乱数(数百万)を生成し、タイミングを開始する前に読んで、CPUのパフォーマンスカウンターを使用することを忘れないでください。正確に測定するには、それらを止めることができます)。

-1

おそらく、乗算と加算の時間にはほとんど違いがありません。逆に、その再帰的性質のために、除算は依然として著しく遅くなり、乗算される。現代のx86アーキテクチャ上の sse命令は、fpuを使用せずに浮動小数点演算を行うときに考慮する必要があります。良いC/C++コンパイラでは、fpuの代わりにsseを使用するオプションが必要です。

1

*/vs +の速度差は、ご使用のプロセッサのアーキテクチャによって異なります。一般的にx86では特に速度の差が現代のプロセッサでは少なくなっています。 *疑問がある場合は+に近いはずです。実験のみです。多くのFP操作で本当に難しい問題がある場合は、ベクタープロセッサとして動作するGPU(GeForce、...)の使用も検討してください。

7

この質問に答える最良の方法は、実際に行う必要がある処理のベンチマーク/プロファイルを書くことです。経験的なものは可能な限り理論的に使用すべきである。特に達成しやすいとき。

あなたはすでにあなたがする必要がある数学の異なる実装がわかっている場合は、数学のいくつかの異なるコードtransfermationsを書き、どこでパフォーマンスのピークを見ることができました。これにより、プロセッサ/コンパイラはプロセッサパイプラインを満たすために異なる実行ストリームを生成し、あなたの答えに具体的な答えを与えることができます。

あなたはDIV/MUL/ADD/SUBの特別パフォーマンスに興味ある場合は、あなたもこれらの命令の変異体を特異的に制御するためのいくつかのインラインアセンブリで投げる可能性タイプの命令が実行されています。しかし、マルチプル実行ユニットをビジー状態にしておき、システムができる性能を知る必要があるかどうかを確認する必要があります。

はまた、あなたは、単にそれらに同じプログラムを実行することにより、プロセッサの複数のバリエーションのパフォーマンスを比較することができるようになる。このような何かをやって、そしてまた、あなたがマザーボードの違いを考慮にする可能性があります。

編集:

+の基本アーキテクチャは同一です。したがって、それらは論理的に同じ時間をかけて計算します。一方、*単一の操作を完了するために、通常は「完全な加算器」から構築された複数のレイヤーが必要です。このサイクルでは、パイプラインごとにa *を発行することができますが、それは加算/減算回路よりもレイテンシが長くなります。 fp /演算は、通常、時間の経過とともに正しい解に向かって反復的に収束する近似法を用いて実施される。これらのタイプの近似は、典型的には乗算によって実現される。したがって、浮動小数点の場合、乗算(これは既に大きな回路であり自己のものである)を多数の乗算回路のパイプラインに「展開」することは実用的ではないため、除算に時間がかかると一般的に考えることができます。依然として、与えられたシステムの性能は、テストによって最も正確に測定されます。

16

理論的には情報はここにある:彼らはリストのすべてのプロセッサに対して

Intel®64 and IA-32 Architectures Optimization Reference Manual, APPENDIX C INSTRUCTION LATENCY AND THROUGHPUT

、FMULのレイテンシはFADDやFDIVのそれに非常に近いです。古いプロセッサの中にはFDIVが2〜3倍遅く、新しいプロセッサではFMULと同じです。

警告:

  1. 私は実際にリンクされたドキュメントは、プロセッサが、それが正しいかどう速いものを作ることを望む何をしますので、あなたが実際の生活の中でこれらの数字に頼ることはできないと言います。

  2. コンパイラが、浮動小数点乗算/除算を使用できる多くの新しい命令セットの1つを使用することを決定する可能性があります。

  3. これはコンパイラの作者が読むことを意図した複雑な文書であり、間違っている可能性があります。私はなぜFDIVレイテンシ番号がCPUの一部で完全に欠落しているのかはわかりません。

+1

非常にクールな文書。一貫性のあるもの(とこの文書で示していること)の1つは、除算が乗算、加算、減算よりもずっと遅いということです。このドキュメントの外観から、倍精度除算のレイテンシは乗算よりも10倍遅いです。たとえば、x = y * 0.5を呼び出す方がx = y/2を呼び出すよりも速くなるはずです。 –

+0

@SteveWortham fdivの情報がfmulよりも10倍遅いことがわかったページを指摘できますか? – 0fnt

+0

@ user247077 - 私は覚えていません。これは数年前のことでした。しかし、このドキュメントには、多くの異なるコマンドのレイテンシを参照するチャートがあります。 FMULは、これらのチャートでFDIVより確かに高速です。その後、C-33ページにDIV r64とMUL r64があり、それらのレイテンシーには大きなギャップがあります。昨年は、乗算と除算の性能差をベンチマークするための64ビットアプリケーションを作成したときに、これらの命令(またはAMDの同等物)をヒットした可能性があります... http://swortham.blogspot.com/2011/10/how -much-faster-is-multiplication-than.html –

関連する問題