コードは次のようになり、内側のループには膨大な時間がかかります。
#define _table_derive ((double*)(Buffer_temp + offset))
#define Table_derive(m,nbol,pos) _table_derive[(m) + 5*((pos) + _interval_derive_dIdQ * (nbol))]
char *Buffer_temp=malloc(...);
for (n_bol=0; n_bol<1400; n_bol++) // long loop here
[lots of code here, hundreds of lines with computations on doubles, other loops, etc]
double ddI=0, ddQ=0;
// This is the original code
for(k=0; k< 100; k++ ) {
ddI += Table_derive(2,n_bol,k);
ddQ += Table_derive(3,n_bol,k);
}
ddI /= _interval_derive_dIdQ;
ddQ /= _interval_derive_dIdQ;
[more code here]
}
oprofile は、実行時間のほとんどがここで費やされていることを示しています (2 番目の列は時間の % です)。
129304 7.6913 :for(k=0; k< 100; k++) {
275831 16.4070 :ddI += Table_derive(2,n_bol,k);
764965 45.5018 :ddQ += Table_derive(3,n_bol,k);
私の最初の質問は、コードが遅い適切な場所を示すために oprofile に頼ることができますか (-Og と -Ofast で試しましたが、基本的に同じです)。
2 番目の質問は、この非常に単純なループが sqrt、atan2、およびその前にある数百行の計算よりも遅いのはなぜですか? すべてのコードを表示しているわけではないことはわかっていますが、コードがたくさんあり、意味がわかりません。
ベクトル化(機能しない)またはアンロール(機能する)するためにさまざまなオプティマイザーのトリックを試しましたが、ほとんど得られませんでした。たとえば、次のようになります。
typedef double aligned_double __attribute__((aligned(8)));
typedef const aligned_double* SSE_PTR;
SSE_PTR TD=(SSE_PTR)&Table_derive(2,n_bol,0); // We KNOW the alignement is correct because offset is multiple of 8
for(k=0; k< 100; k++, TD+=5) {
#pragma Loop_Optimize Unroll No_Vector
ddI += TD[0];
ddQ += TD[1];
}
オプティマイザの出力を確認しました: "-Ofast -g -march=native -fopt-info-all=missed.info -funroll-loops" この場合、"loop unrolled 9 times" が表示されますが、ベクトル化しようとすると、(要するに)次のようになります。 ) _1328 + (long unsigned int) (n_bol_1173 * 500) * 2) * 4)"
これをスピードアップする方法はありますか?
補遺:コメントをありがとう、私はここで答えようとします:
- はい、私はコードが醜いことを知っています (それは私のものではありません)。
- Cコードがライブラリにあり、Cによって処理および変更されると大きな配列が呼び出し元(IDL、Python、またはC)に渡されるため、私はこの配列にこだわっています。
- char* を複雑な多次元の double* にキャストする代わりに、いくつかの構造体を使用する方がよいことはわかっていますが、上記を参照してください。このプログラムが最初に作成されたとき、構造体は C 仕様の一部ではなかった可能性があります (冗談です... 多分)
- ベクトライザーの場合、構造体の配列よりも配列の構造体の方が良いことはわかっていますが、ため息...上記を参照してください。
- (呼び出しプログラム内に) 実際の外部ループがあるため、このモノリシック配列の合計サイズは約 2Gb です。
- 現状では、最適化なしで実行するのに約 15 分かかり、いくつかのコードを書き直してから 1 分後 (atan2 の高速化、配列内の手動整列など)、-Ofast と -march=native を使用しました。
- ハードウェアの制約が変更されたため、データフローに追いつくために高速化を試みています。
- Clang を試してみたところ、ゲインはわずか (数秒) でしたが、-fopt-info などの最適化レポートを取得するオプションが表示されません。何が起こっているのかを知る唯一のオプションとしてアセンブリを見る必要がありますか?
- システムは 500Gb の RAM を備えた猛烈な 64 コアですが、上記のコードを並列化するための OpenMP プラグマを挿入することはできませんでした (私は試しました): ファイルを読み取り、メモリ内で完全に解凍します (2Gb) 、それを順番に分析し (「+=」など)、呼び出し元の IDL/Python にいくつかの結果を吐き出します。すべてが単一のコア上にあります (ただし、他のコアは実際の取得と後処理でかなりビジーです)。:(
- 役に立たない、素晴らしい提案をありがとう: ddQ += ... を削除すると、時間の割合が前の行に転送されるようです: 376280 39.4835:ddI+=...
- これにより、さらに良い結果が得られます。両方を削除すると(したがってループ全体が)保存されます...何もありません!!! ピーターが言ったように、私はプロファイラーを信用できないと思います。ループレス プログラムをプロファイリングすると、タイミングがより均等に分散されます (以前は 1 秒を超える 3 行だけでしたが、現在は約 10 行で、単純な変数の割り当てのようにすべて無意味です)。
内側のループは最初からニシンだったと思います。手動タイミングを使用して最適化を再開します。ありがとう。