この質問を読んだとき、誰かが(何年も前に)アセンブラーの観点から、これらの2つの操作は非常に異なると私に言ったことを思い出しました。
n = 0;
n = n - n;
これは本当ですか?もしそうなら、なぜそうなのですか?
編集:いくつかの返信で指摘されているように、コンパイラが同じものに最適化するのはかなり簡単だと思います。しかし、私が興味深いと思うのは、コンパイラが完全に一般的なアプローチを採用した場合に、なぜそれらが異なるのかということです。
この質問を読んだとき、誰かが(何年も前に)アセンブラーの観点から、これらの2つの操作は非常に異なると私に言ったことを思い出しました。
n = 0;
n = n - n;
これは本当ですか?もしそうなら、なぜそうなのですか?
編集:いくつかの返信で指摘されているように、コンパイラが同じものに最適化するのはかなり簡単だと思います。しかし、私が興味深いと思うのは、コンパイラが完全に一般的なアプローチを採用した場合に、なぜそれらが異なるのかということです。
よく使うアセンブラ コードの記述:
xor eax, eax
それ以外の
mov eax, 0
これは、最初のステートメントにはオペコードのみがあり、関連する引数がないためです。あなたのCPUはそれを(2ではなく)1サイクルで行います。あなたのケースは似ていると思います(サブを使用していますが)。
コンパイラ VC++ 6.0、最適化なし:
4: n = 0;
0040102F mov dword ptr [ebp-4],0
5:
6: n = n - n;
00401036 mov eax,dword ptr [ebp-4]
00401039 sub eax,dword ptr [ebp-4]
0040103C mov dword ptr [ebp-4],eax
最適化コンパイラは、この 2 つに対して同じアセンブリ コードを生成します。
初期の頃は、メモリと CPU のサイクルが不足していました。これは、いわゆる「のぞき穴の最適化」の多くにつながります。コードを見てみましょう:
move.l #0,d0 moveq.l #0,d0 sub.l a0,a0
最初の命令では、オペコードに 2 バイト、次に値 (0) に 4 バイトが必要です。つまり、4 バイトが無駄になり、メモリに 2 回アクセスする必要があります (オペコード用に 1 回、データ用に 1 回)。遅い。
moveq.l
データをオペコードにマージするため、より優れていましたが、0から7の間の値しかレジスタに書き込むことができませんでした. また、データ レジスタのみに制限されていたため、アドレス レジスタをすばやくクリアする方法はありませんでした。データレジスタをクリアしてから、データレジスタをアドレスレジスタにロードする必要があります(2つのオペコード。悪い)。
これは、任意のレジスタで機能する最後の操作につながり、1 回のメモリ読み取りで 2 バイトしか必要としません。Cに翻訳すると、次のようになります
n = n - n;
これは、最も頻繁に使用されるタイプn
(整数またはポインター)で機能します。
n
が as として宣言されているかどうかによって異なりますvolatile
。
レジスターをそれ自体から減算するか、それ自体と XOR することによってレジスターをゼロにするアセンブリ言語の手法は興味深いものですが、実際には C に変換されません。
最適化する C コンパイラは、理にかなっていればこの手法を使用しますが、明示的に書き出そうとしても、何も達成できない可能性があります。
組み立てなどはよくわかりませんが、概ね、
n=0
n=n-n
n が浮動小数点の場合、常に等しいとは限りません。ここを参照して ください http://www.codinghorror.com/blog/archives/001266.html