2

私のgccのバグ?私のコードのバグ?両方とも?

http://files.minthos.com/code/speedtest_doubles_wtf.cpp

どういうわけか、それは関数を「最適化」して、doubleの配列をゼロにしてq6600で2.6秒かかるようにします。より複雑な関数が配列をある程度意味のあるもので満たすのにかかる33ミリ秒ではありません。

他の人が同様の結果を得るかどうか、もしそうなら、誰かが何が起こっているのかを説明できるかどうかを知りたいと思います。また、整数と浮動小数点のパフォーマンスの大きな違いの原因を突き止めます(特に最適化せずにコンパイルする場合)。

4

3 に答える 3

5

99 行目:

memcpy(floats, ints, sizeof(floats));

floats[]浮動小数点ガベージで部分的に効果的に初期化しています。残りはゼロのままです。これは、float を整数ビットマップに置き換えてから、それらを double として解釈することに起因します。オーバーフローとアンダーフローがパフォーマンスに影響を与えているのではないでしょうか? テストするために、再現性のために乱数シードを定数 1000 に変更し、次の結果を得ました。

[wally@zenetfedora Downloads]$ ./speedtest_doubles_wtf.cpp
no optimization
begin: 0.017000
floats: 27757.816000
ints: 28117.604000
floats: 40346.196000
ints: 41094.988000
sum: 7999999.998712
sum2: 67031739228347449344.000000
mild optimization
begin: 0.014000
floats: 68.574000
ints: 68.609000
floats: 147.105000
ints: 820.609000
sum: 8000000.000001
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.014000
floats: 73.588000
ints: 73.623000
floats: 144.105000
ints: 1809.980000
sum: 8000000.000001
sum2: 67031739228347441152.000000
again, now using ffun2()
no optimization
begin: 0.017000
floats: 22720.648000
ints: 23076.134000
floats: 35480.824000
ints: 36229.484000
floats: 46324.080000
sum: 0.000000
sum2: 67031739228347449344.000000
mild optimization
begin: 0.013000
floats: 69.937000
ints: 69.967000
floats: 138.010000
ints: 965.654000
floats: 19096.902000
sum: 0.000000
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.015000
floats: 95.851000
ints: 95.896000
floats: 206.594000
ints: 1699.698000
floats: 29382.348000
sum: 0.000000
sum2: 67031739228347441152.000000

memcpy を適切な割り当てに置き換えた後に繰り返すことで、型変換が発生する可能性があり、浮動小数点境界条件を防ぐ必要があります。

for(int i = 0; i < 16; i++)
{
    ints[i] = rand();
    floats[i]= ints[i];
}

変更されたプログラムは、ランダム シードとして定数 1000 を使用して、次の結果を提供します。

[wally@zenetfedora Downloads]$ ./speedtest_doubles_wtf.cpp
no optimization
begin: 0.013000
floats: 35814.832000
ints: 36172.180000
floats: 85950.352000
ints: 86691.680000
sum: inf
sum2: 67031739228347449344.000000
mild optimization
begin: 0.013000
floats: 33136.644000
ints: 33136.678000
floats: 51600.436000
ints: 52494.104000
sum: inf
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.013000
floats: 31914.496000
ints: 31914.540000
floats: 48611.204000
ints: 49971.460000
sum: inf
sum2: 67031739228347441152.000000
again, now using ffun2()
no optimization
begin: 0.014000
floats: 40202.956000
ints: 40545.120000
floats: 104679.168000
ints: 106142.824000
floats: 144527.936000
sum: inf
sum2: 67031739228347449344.000000
mild optimization
begin: 0.014000
floats: 33365.716000
ints: 33365.752000
floats: 49180.112000
ints: 50145.824000
floats: 80342.648000
sum: inf
sum2: 67031739228347441152.000000
heavier optimization
begin: 0.014000
floats: 31515.560000
ints: 31515.604000
floats: 47947.088000
ints: 49016.240000
floats: 78929.784000
sum: inf
sum2: 67031739228347441152.000000

これは 2004 年頃の古い PC で、それ以外は負荷が軽いです。

それが問題を遅くしたようです。おそらく、算術を行うためのゼロが少なくなりますか? これは、多くのランダムなビット パターンがどのように見えるかです。または、0.000000000000000000000000382652 のような値。それがたとえば 0.1 に追加されると、下位ビットが削除される傾向があります。

于 2010-09-03T00:43:54.947 に答える
1

rand()は 32 ビット値を返すため (少なくとも 32 ビット プラットフォームの gcc では)、ランダムな 64 ビット整数はすべて上位 32 ビットにゼロがあります。したがって、再解釈された整数のビット パターンでは指数フィールドがゼロになるため、すべての double は非正規化されます。非正規化された値に 0.1 を追加すると、正規化された値 (0.1 に非常に近い) が得られます。

したがって、 の各行ffun2は非正規化値による乗算です。の各行ffun3は、正規化された値による乗算です。生成されたアセンブリを見ると、ループの前に乗数が計算されていることがわかります。いずれの場合も、ループは乗算だけで構成されています。実行時間の違いの最も可能性の高い説明は、乗数が非正規化されている場合、乗算にかかる時間がはるかに長くなることです。

最後の質問ですが、浮動小数点演算 (特に倍精度) は整数演算よりもはるかに複雑であるため、最新のパイプライン プロセッサでは、各命令の実行に時間がかかります。

于 2010-09-03T01:39:30.617 に答える
1

ベンチマーク間でリセットしていないbeginため、タイミングの数値を解釈するのは困難です。多分これがあなたの混乱の原因ですか?

于 2010-09-03T00:43:14.790 に答える