2008-09-10 2 views
6

"ans = n * 3"または "ans = n +(n * 2)"という高速コードにコンパイルされますか?"n * 3"または "n +(n * 2)"という高速コードにコンパイルされますか?

nがintまたはlongのいずれかであり、最新のWin32 Intelボックスで実行されていると仮定します。

逆参照に関連するものがある場合は、これが異なるでしょうか。つまり、どちらが高速でしょうか。

 

long a; 
long *pn; 
long  ans; 

... 
*pn = some_number; 
ans = *pn * 3; 

それとも

 
ans = *pn+(*pn*2); 

それとも、それは1つが最適化コンパイラは、どのような場合にはこれを考慮する可能性があるとして、心配する必要はない何かありますか?

答えて

55

IMOのようなマイクロ最適化は、エキゾチックなコンパイラを使用しない限り必要ありません。私は最初に読みやすさを置いています。

1

実際に使用しているコンパイラによって異なりますが、おそらく同じコードに変換されます。

小さなテストプログラムを作成し、その逆アセンブリを確認することで、自分で確認できます。

1

ほとんどのコンパイラは、整数乗算を一連のビットシフトと加算に分解するのに十分スマートです。 Windowsのコンパイラについてはわかりませんが、少なくともgccではアセンブラを吐き出すことができます。もしそれを見ると、両方の方法で同じアセンブラを見ることができます。

0

コンパイラはあなたのようなコードを最適化しています。現代のコンパイラはどちらの場合も同じコードを生成し、さらに* 2を左シフトで置き換えます。

+0

私は組み込みソフトウェア開発のためのいくつかの本当に変わったコンパイラを見ました。 – aku

+1

組み込みシステムでは、ほぼすべての従来の知恵が終了します。 ;-) –

4

これは、コンパイラ、その設定、および周囲のコードによって異なります。

測定を行わずに物事が「高速」であるかどうか試してみるべきではありません。一般に

あなたは、最近、ナノスケールの最適化原料のこの種の心配べきではない - それは、ほとんどの場合、完全な見当違いだ、とあなたは本当にそれが重要だっドメインで働いていた場合、あなたはすでにプロファイラを使用して見ていることでしょうコンパイラのアセンブリ言語の出力で

10

自分で測定するのは簡単ですが、そのようにしないでください。 (cygwinのからgcctimeを使用)

/* test1.c */ 
int main() 
{ 
    int result = 0; 
    int times = 1000000000; 
    while (--times) 
     result = result * 3; 
    return result; 
} 

machine:~$ gcc -O2 test1.c -o test1 
machine:~$ time ./test1.exe 

real 0m0.673s 
user 0m0.608s 
sys  0m0.000s 

回のカップルのためのテストを行うと、他のケースのために繰り返します。

あなたはアセンブリコードで覗いしたい場合は、gcc -S -O2 test1.c

+0

残念ながら、これは悪い例です - i686-apple-darwin8-gcc-4.0.1では、ループから「結果=結果* 3」が完全に削除されるため、ループは常にゼロです。初期条件を "result = 1"に変更するとより良い結果が得られます。 –

+0

以上で乱数の配列を作成して処理するので、コンパイラは何の仮定もできません。 – DarenW

15

それは問題ではありません。最近のプロセッサは、MULを実行して複数のサイクルを使用するために一連のシフトを実行し、内部で追加する必要があった古いプロセッサーとは異なり、1クロックサイクル以下で整数MUL命令を実行できます。私は

MUL EAX,3 

は(はい、これはIntelプロセッサにバイアスされているが、ある最適化のこの種が有用であったかもしれない最後のプロセッサは、おそらく486だった

MOV EBX,EAX 
SHL EAX,1 
ADD EAX,EBX 

より速く実行されることを賭けるだろうおそらく他のアーキテクチャの代表でもあります)。

いずれにせよ、合理的なコンパイラは、最小/最速のコードを生成できるはずです。常に読みやすさを優先してください。

+3

あなたが使用できるレジスタのレイテンシと柔軟性が考慮されると、MULがより高速に実行されることは本当に疑問です。さらに、x86上で、あなたが与えた3命令シーケンスではなく、LEAは、3 * nとn + 2 * nのために適切なコンパイラによって使用されます。 –

+1

Trueですが、LEAは小さな定数セット(正しく呼び出すと2、3、4、5、8&9)を乗算する場合にのみ有効です。とにかく私のポイントは、コンパイラに最速のコードを理解させることでした。 – Ferruccio

4

あなたのコードでコンパイラが何をしているのかを知ることは難しくありません。私はここでDevStudio 2005を使っています。次のコードを使用して簡単なプログラムを作成します。

中間行にブレークポイントを置き、デバッガを使用してコードを実行します。ブレークポイントがトリガされたら、ソースファイルを右クリックし、 "Disassemblyに移動"を選択します。これで、CPUが実行しているコードのウィンドウが表示されます。この場合、最後の2行はまったく同じ命令、すなわち "lea eax、[ebx + ebx * 2]"(この特定のケースではビットシフトと加算ではありません)を生成します。現代のIA32 CPUでは、CPUのパイプライン化のためにビットシフトではなく、まっすぐなMULを行う方が効率的です。変更された値をあまりにも早く使用するとペナルティが発生します。

これは、akuが何を話しているかを示しています。つまり、コンパイラはあなたのコードに最適な命令を選択するのに十分なほど巧妙です。

+0

私はパイプラインが問題ではありません。演算ユニットはおそらく、ebx + ebx * 2を1ステップで内部的に実行することができます。 – Artelius

0

コンパイラを信頼して、そのようなコードを最適化します。コードレベルでの可読性ははるかに重要です。真の最適化はより高いレベルになるはずです。

1

気にしません。私は、より重要なことが最適化されると思っています。自分でコーディングしてテストするのではなく、思考に投資してその質問を書くのにどれくらいの時間を費やしましたか?

:-)

限り、あなたはまともな最適化コンパイラを使用しているよう
1

、コンパイラはを理解するのは簡単ですちょうど書き込みコード。これにより、コンパイラーが賢明な最適化を実行しやすくなります。

この質問をすると、最適化コンパイラが最適化の詳細を知っていることを示しています。コンパイラを信頼してください。 n * 3を使用してください。

this answerもご覧ください。

関連する問題