30

10 ~ 20 年前は、乗算と除算を使用せずに加算と減算を使用する数値コードを記述することは価値がありました。良い例は、多項式を直接計算する代わりに、前方差分を使用して多項式曲線を評価することです。

これは今でも当てはまりますか、それとも現代のコンピューター アーキテクチャは、*,/ が +,- よりも何倍も遅くないところまで進歩していますか?

具体的には、ソフトウェアで FP を実行しようとする小さなマイクロではなく、広範なオンボード浮動小数点ハードウェアを備えた最新の典型的な x86 チップで実行されるコンパイル済みの C/C++ コードに興味があります。パイプライン処理やその他のアーキテクチャの強化により、特定のサイクル カウントが妨げられていることは理解していますが、有用な直感を得たいと思っています。

4

6 に答える 6

26

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

たとえば、次のループを考えてみましょう。

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

NUMITER = 10 ^ 7、NUMEL = 10 ^ 2の場合、両方の配列が小さな正の数に初期化されます(NaNははるかに低速です)。これには、64ビットプロシージャでdoubleを使用すると6.0秒かかります。ループを次のように置き換えると

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

たった1.7秒しかかかりません...したがって、追加を「やり過ぎた」ので、mulsは本質的に無料でした。と追加の削減が役立ちました。それはもっと混乱します:

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

--同じmul/add分布ですが、定数が乗算されるのではなく加算されるようになりました--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
于 2009-07-18T18:22:47.973 に答える
19

理論的には、情報は次のとおりです。

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

リストされているすべてのプロセッサについて、FMUL のレイテンシは FADD または FDIV のレイテンシに非常に近いです。一部の古いプロセッサでは、FDIV はそれよりも 2 ~ 3 倍遅くなりますが、新しいプロセッサでは FMUL と同じです。

警告:

  1. 私がリンクしたドキュメントは、実際にはこれらの数値に頼ることはできないと述べています。これは、プロセッサーが正しい場合、物事をより速くしたいことを実行するためです。

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

  3. これは、コンパイラの作成者だけが読むことを意図した複雑なドキュメントであり、間違っている可能性があります。一部の CPU で FDIV レイテンシの数値が完全に欠落している理由がよくわかりません。

于 2009-07-18T17:22:22.213 に答える
9

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

実行する必要のある数学のさまざまな実装をすでに知っている場合は、数学のいくつかの異なるコード転送を記述して、パフォーマンスがピークになる場所を確認できます。これにより、プロセッサ/コンパイラがさまざまな実行ストリームを生成してプロセッサパイプラインを埋め、答えに対する具体的な答えを得ることができます。

特にDIV/MUL / ADD / SUBタイプの命令のパフォーマンスに関心がある場合は、インラインアセンブリをトスして、これらの命令のどのバリアントが実行されるかを具体的に制御することもできます。ただし、システムのパフォーマンスを把握するには、複数の実行ユニットをビジー状態に保つ必要があります。

また、このようなことを行うと、同じプログラムを実行するだけで、プロセッサの複数のバリエーションのパフォーマンスを比較でき、マザーボードの違いを考慮に入れることもできます。

編集:

+-の基本的なアーキテクチャは同じです。したがって、論理的には計算に同じ時間がかかります。*一方、単一の操作を完了するには、通常は「全加算器」で構成される複数の層が必要です。これは、サイクルごとにパイプラインに*を発行できますが、加算/減算回路よりもレイテンシーが高くなることを保証します。fp /演算は通常、時間の経過とともに正解に向かって反復的に収束する近似法を使用して実装されます。これらのタイプの近似は、通常、乗算によって実装されます。したがって、浮動小数点の場合、乗算(すでに大きな回路であり、それ自体である)を多数の乗算回路のパイプラインに「展開」することは非現実的であるため、一般に除算に時間がかかると想定できます。

于 2009-07-18T16:35:13.947 に答える
2

決定的なリファレンスを見つけることはできませんが、大規模な実験によると、今日の浮動小数点乗算は足し算や引き算とほぼ同じ速度ですが、除算はそうで​​はありません (ただし、「何倍も」遅くはありません)。独自の実験を実行することによってのみ、必要な直感を得ることができます。事前に乱数 (数百万個) を生成し、タイミングを開始する前にそれらを読み取り、CPU のパフォーマンス カウンターを使用することを忘れないでください (他のプロセスが実行されていないため、正確な測定のために!

于 2009-07-18T02:15:45.307 に答える
1

* / vs + - の速度の違いは、プロセッサのアーキテクチャによって異なります。一般に、特に x86 では、最新のプロセッサでは速度の差が小さくなっています。* は + に近づける必要があります。疑わしい場合は、試してみてください。多くの FP 操作で非常に難しい問題がある場合は、ベクトル プロセッサとして機能する GPU (GeForce など) の使用も検討してください。

于 2009-07-18T02:29:05.067 に答える
-1

掛け算と足し算の時間の差はおそらくほとんどありません。一方、除算は再帰的な性質のため、乗算よりもかなり遅くなります。最近の x86 アーキテクチャでは、fpu を使用するのではなく、浮動小数点演算を行うときに sse 命令を考慮する必要があります。

于 2009-07-18T02:21:47.747 に答える