2012-04-09 31 views
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) 
} 

何かが私には明らかではない:優れているの?私は、他のバージョンを使用してより速く動作するものは見えません。私はここに何かを逃していますか

私が見るすべては、各命令が前の命令に依存していることである は私が前の命令が次々に開始するためにフィニッシュすることを待つ必要があることを意味...

おかげ

+1

どの言語ですか? – Bytemain

+0

ウィキペディアには、ループアンローリングのアイデアがあります。http://en.wikipedia.org/wiki/Loop_unwinding –

+0

一般的に、これらは同等ではありません。 A(i)でなければならない。 B(i); C(i); A(i + 1); B(i + 1); – gnasher729

答えて

9

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

、それはのようなものです:それはのようなものです第二に

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

:あなたは後者の場合には見ることができます

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

、テストとジャンプのオーバーヘッドはわずかです最初の1命令は1命令あたり1命令です。だからもっと頻繁に起こる。

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

3

このコードが「より良い」か「悪い」かは、A,BCの実装に依存します。使用するコンパイラ、使用しているハードウェア、実行中のハードウェアはそれぞれnです。

通常、ループアンローリングの利点は、ループを行うことのオーバーヘッド(つまり、iを増加させ、それをnと比較する)が削減されることです。この場合、3の因数で減らすことができます。

4

ループのアンロールは、ループを高速化する可能性があるがバイナリのサイズを増加させる可能性のある分岐命令&分岐命令の数を減らすために使用されます。実装とプラットフォームによってはどちらかが高速になる可能性があります。

2

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

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

0

一般的に、最適化を「発明」しようとするのは良い考えではありません。通常、そのような証拠を得るための最良の方法は、良いプロファイラを使用することです。私は、このコードの両方のバージョンをプロファイラでテストして、違いを確認します。

また、前述のように、非常にprotableイマイチアンロール何度もループ、それはプラットフォームに大きく依存し、コンパイラなど

あなたはさらにコンパイラオプションで遊ぶことができます。興味深いgccオプションは "-floop-optimize"です。これは自動的に "-O、-O2、-O3、-Os"となります。

EDITまた、 "-funroll-loops"コンパイラオプション。

+0

また、これはかなり簡潔ですばらしいループアンロールの例を見てください:[Duffのデバイス](http://en.wikipedia.org/wiki/Duff%27s_device) – Brady