また、命令の組み合わせにも依存します。プロセッサには常に複数の計算ユニットが待機しており、それらすべてが常に満たされている場合に最大のスループットが得られます。したがって、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