28

TL; DR:データの乗算/キャストがsize_t遅いのはなぜですか?また、これがプラットフォームごとに異なるのはなぜですか?

完全には理解できないパフォーマンスの問題が発生しています。コンテキストは、128x128のuint16_t画像が読み取られ、数100Hzのレートで後処理されるカメラフレームグラバーです。

後処理では、2 ^ 16の要素を持つヒストグラムを生成しframe->histoますuint32_tthismaxval基本的に、すべての強度値を集計します。このヒストグラムを使用して、合計と2乗合計を計算します。

double sum=0, sumsquared=0;
size_t thismaxval = 1 << 16;

for(size_t i = 0; i < thismaxval; i++) {
    sum += (double)i * frame->histo[i];
    sumsquared += (double)(i * i) * frame->histo[i];
}

プロファイルを使用してコードをプロファイリングすると、次のようになります(サンプル、パーセンテージ、コード)。

 58228 32.1263 :  sum += (double)i * frame->histo[i];
116760 64.4204 :  sumsquared += (double)(i * i) * frame->histo[i];

または、最初の行がCPU時間の32%を占め、2番目の行が64%を占めます。

私はいくつかのベンチマークを行いましたが、問題があるのはデータ型/キャストのようです。コードをに変更すると

uint_fast64_t isum=0, isumsquared=0;

for(uint_fast32_t i = 0; i < thismaxval; i++) {
    isum += i * frame->histo[i];
    isumsquared += (i * i) * frame->histo[i];
}

実行速度は約10倍です。ただし、このパフォーマンスへの影響はプラットフォームによっても異なります。ワークステーションでは、Core i7 CPU 950@3.07GHzのコードは10倍高速です。Intel Core i7 Sandy Bridge 2.7 GHz(2620M)を搭載した私のMacbook8,1では、コードはわずか2倍高速です。

今私は疑問に思っています:

  1. 元のコードが非常に遅く、簡単に高速化されるのはなぜですか?
  2. なぜこれはプラットフォームごとに大きく異なるのですか?

アップデート:

私は上記のコードをでコンパイルしました

g++ -O3  -Wall cast_test.cc -o cast_test

Update2:

最適化されたコードをプロファイラー(Mac上のInstruments 、 Sharkなど)で実行したところ、次の2つが見つかりました。

1)ループ自体にかなりの時間がかかる場合があります。thismaxvalタイプsize_tです。

  1. for(size_t i = 0; i < thismaxval; i++)総実行時間の17%を占める
  2. for(uint_fast32_t i = 0; i < thismaxval; i++)3.5%かかります
  3. for(int i = 0; i < thismaxval; i++)プロファイラーには表示されません。0.1%未満だと思います

2)データ型とキャストは次のように重要です。

  1. sumsquared += (double)(i * i) * histo[i];15%(ありsize_t i
  2. sumsquared += (double)(i * i) * histo[i];36%(ありuint_fast32_t i
  3. isumsquared += (i * i) * histo[i];13%(with uint_fast32_t iuint_fast64_t isumsquared
  4. isumsquared += (i * i) * histo[i];11%(with int iuint_fast64_t isumsquared

驚いたことに、intより速いですuint_fast32_tか?

Update4:

1台のマシンで、さまざまなデータ型とさまざまなコンパイラを使用して、さらにいくつかのテストを実行しました。結果は以下のとおりです。

testd 0-2の場合、関連するコードは次のとおりです。

    for(loop_t i = 0; i < thismaxval; i++)
        sumsquared += (double)(i * i) * histo[i];

sumsquareddouble、およびloop_t size_tuint_fast32_tを使用しint、テスト0、1、および2の場合。

テスト3〜5の場合、コードは次のとおりです。

    for(loop_t i = 0; i < thismaxval; i++)
        isumsquared += (i * i) * histo[i];

isumsquaredタイプと再び、uint_fast64_tそしてテスト3、4、5の場合。loop_tsize_tuint_fast32_tint

私が使用したコンパイラは、gcc 4.2.1、gcc 4.4.7、gcc 4.6.3、およびgcc4.7.0です。タイミングはコードの合計CPU時間のパーセンテージで表されるため、絶対的なパフォーマンスではなく、相対的なパフォーマンスを示します(ただし、実行時間は21秒で非常に一定でした)。プロファイラーが2行のコードを正しく分離したかどうかはよくわからないため、CPU時間は両方の2行です。

gcc:4.2.1 4.4.7 4.6.3 4.7.0
----------------------------------
テスト0:21.85 25.15 22.05 21.85
テスト1:21.9 25.05 22 22
テスト2:26.35 25.1 21.95 19.2
テスト3:7.15 8.35 18.55 19.95
テスト4:11.1 8.45 7.35 7.1
テスト5:7.1 7.8 6.9 7.05

また:

鋳造性能

これに基づくと、使用する整数型に関係なく、キャストにはコストがかかるようです。

また、gcc 4.6および4.7はループ3(size_tおよびuint_fast64_t)を適切に最適化できないようです。

4

2 に答える 2

4

元の質問の場合:

  1. 整数から浮動小数点データ型への変換が含まれるため、コードは低速です。そのため、合計変数に整数データ型も使用すると、浮動小数点変換が不要になるため、簡単に高速化できます。
  2. 違いはいくつかの要因の結果です。たとえば、プラットフォームがint->float変換をどれだけ効率的に実行できるかによって異なります。さらに、この変換は、プログラムフローと予測エンジン、キャッシュなどのプロセッサ内部の最適化を台無しにする可能性があります。また、プロセッサの内部並列化機能は、このような計算に大きな影響を与える可能性があります。

追加の質問について:

  • 「驚くべきことに、intはuint_fast32_tよりも高速です」?プラットフォームのsizeof(size_t)とsizeof(int)は何ですか?私が推測できるのは、どちらもおそらく64ビットであるため、32ビットにキャストすると計算エラーが発生するだけでなく、異なるサイズのキャストのペナルティも含まれるということです。

一般に、これらが本当に必要でない場合は、表示および非表示のキャストをできるだけ避けるようにしてください。たとえば、環境(gcc)の「size_t」の背後に隠されている実際のデータ型を見つけて、それをループ変数に使用してみてください。あなたの例では、uintの2乗をfloatデータ型にすることはできないため、ここでdoubleを使用しても意味がありません。最大のパフォーマンスを達成するには、整数型に固執します。

于 2012-05-16T13:47:36.183 に答える
1

x86では、、、およびを変換する命令しかないため、浮動小数点への変換は遅くuint64_tなります。32ビットモードでは、SSEではなくx87命令を使用してのみ変換できます。int64_tint32_tint16_tint16_tint64_t

uint64_t浮動小数点に変換する場合、GCC 4.2.1は最初に値をanであるかのように変換し、次にそれが負の場合は264を加算して補正しますint64_t。(x87を使用する場合、Windowsおよび* BSDで、または精度制御を変更した場合、変換は精度制御を無視しますが、追加はそれを尊重することに注意してください。)

Anuint32_tは最初にに拡張されint64_tます。

特定の64ビット機能を備えたプロセッサで64ビット整数を32ビットモードで変換する場合、ストアからロードへの転送の問題によりストールが発生する可能性があります。64ビット整数は2つの32ビット値として書き込まれ、1つの64ビット値として読み戻されます。変換が長い依存関係チェーンの一部である場合(この場合ではない)、これは非常に悪い場合があります。

于 2012-05-17T19:59:39.037 に答える