5

コードを考えると:

for (int i = 0; i < n; ++i) 
{ 
  A(i) ; 
  B(i) ; 
  C(i) ; 
}

そして最適化バージョン:

for (int i = 0; i < (n - 2); i+=3) 
{ 
  A(i) 
  A(i+1) 
  A(i+2) 
  B(i) 
  B(i+1) 
  B(i+2) 
  C(i) 
  C(i+1) 
  C(i+2)
}

よくわからないことがあります: どちらが良いですか? 他のバージョンを使用すると、これ以上高速に動作するものは見当たりません。ここで何か不足していますか?

私が見るのは、各命令が前の命令に依存していることです。つまり、次の命令を開始するには、前の命令が終了するのを待つ必要があります...

ありがとう

4

5 に答える 5

9

言語の高レベルのビューでは、最適化は表示されません。速度の向上は、コンパイラがあなたの持っているもので何をするかによってもたらされます。

最初のケースでは、次のようになります。

LOCATION_FLAG;
DO_SOMETHING;
TEST FOR LOOP COMPLETION;//Jumps to LOCATION_FLAG if false

2番目の例では次のようになります。

LOCATION_FLAG;
DO_SOMETHING;
DO_SOMETHING;
DO_SOMETHING;
TEST FOR LOOP COMPLETION;//Jumps to LOCATION_FLAG if false

後者の場合、テストとジャンプのオーバーヘッドは3つにつき1つの命令のみであることがわかります。最初の場合、1つにつき1つの命令です。そのため、より頻繁に発生します。

したがって、信頼できる不変条件(mod 3の配列、例を使用)がある場合は、基になるアセンブリがより直接的に記述されるため、ループをほどく方が効率的です。

于 2012-04-09T22:02:38.707 に答える
4

ループ展開は、ジャンプと分岐命令の数を減らすために使用されます。これにより、ループが高速になる可能性がありますが、バイナリのサイズが大きくなります。実装とプラットフォームに応じて、どちらかが高速になる可能性があります。

于 2012-04-09T22:04:08.520 に答える
3

さて、このコードが「より良い」か「より悪い」かはA、 、 、Bおよびの実装、期待するC値、使用しているコンパイラ、および実行しているハードウェアに完全に依存します。n

通常、ループ展開の利点は、ループを実行する (つまり、ループを増やしてiと比較するn) オーバーヘッドが削減されることです。この場合、3 分の 1 に減らすことができます。

于 2012-04-09T22:01:25.203 に答える
2

関数 A()、B()、および C() が同じデータセットを変更しない限り、2 番目のバージョンはより多くの並列化オプションを提供します。

最初のバージョンでは、相互依存関係がないと仮定して、3 つの関数を同時に実行できました。2 番目のバージョンでは、3 つの関数すべてを 3 つのデータセットすべてで同時に実行できました。これを実行するのに十分な実行ユニットがあり、相互依存関係がないことを前提としています。

于 2012-04-09T22:07:03.607 に答える
0

一般に、最適化を「考案」しようとするのは良い考えではありません。ただし、増加するという明確な証拠がない限り、多くの場合、結果として劣化が発生する可能性があるためです。通常、このような証拠を取得する最善の方法は、優れたプロファイラーを使用することです。このコードの両方のバージョンをプロファイラーでテストして、違いを確認します。

また、前述のように、多くの場合、ループ展開はあまり有利ではありません。プラットフォーム、コンパイラなどに大きく依存します。

さらに、コンパイラ オプションで遊ぶことができます。興味深い gcc オプションは「-floop-optimize」で、「-O、-O2、-O3、および -Os」で自動的に取得されます。

EDITさらに、「-funroll-loops」コンパイラ オプションを見てください。

于 2012-04-10T07:18:23.767 に答える