私はこれらのコンパイラーの両方を異なるプロジェクトで使用しました。
コード処理と出力生成の点でどのように違いますか?たとえば、gcc
とclang
の両方に-O2
最適化のオプション。コードを最適化するという点で、それらは同じ方法(高レベル)で動作していますか?たとえば、次のコードがある場合は、少しテストを行いました。
int foo(int num) {
if(num % 2 == 1)
return num * num;
else
return num * num +1;
}
以下は、clangを使用した出力アセンブリと-O2を使用したgccです。
----gcc 5.3.0----- ----clang 3.8.0----
foo(int): foo(int):
movl %edi, %edx movl %edi, %eax
shrl $31, %edx shrl $31, %eax
leal (%rdi,%rdx), %eax addl %edi, %eax
andl $1, %eax andl $-2, %eax
subl %edx, %eax movl %edi, %ecx
cmpl $1, %eax subl %eax, %ecx
je .L5 imull %edi, %edi
imull %edi, %edi cmpl $1, %ecx
leal 1(%rdi), %eax setne %al
ret movzbl %al, %eax
.L5: addl %edi, %eax
movl %edi, %eax retq
imull %edi, %eax
ret
ご覧のとおり、出力には異なる命令が含まれています。だから私の質問は、それらのうちの1つが別のプロジェクトで別のものよりも有利ですか?
この場合、Clang出力は分岐しませんので、より良いです。代わりに、num % 2 == 1
の値をal
にロードします。gccによって生成されたコードはジャンプを使用します。 num
が50%の確率で偶数/奇数であり、繰り返しパターンがないと予想される場合、GCCによって生成されるコードは 分岐予測エラーの影響を受けやすい になります。
ただし、GCCでもコードを適切に動作させることができます。
int foo(int num) {
return num * num + (num % 2 != 1);
}
さらに、アルゴリズムは符号なしの数値のみに対して実際に定義されているように見えるため、unsigned int
(負の数値では異なる)を使用する必要があります。実際には、unsigned int
GCC/Clangがnum % 2
をnum & 1
に最適化できるように、引数
unsigned int foo(unsigned int num) {
return num * num + (num % 2 != 1);
}
gcc -O2
によって生成された結果のコード
movl %edi, %edx
imull %edi, %edi
andl $1, %edx
xorl $1, %edx
leal (%rdi,%rdx), %eax
ret
どちらのコンパイラによって生成された元の関数のコードよりもはるかに優れています。したがって、コンパイラーは、自分が何をしているかを知っているプログラマーほど重要ではありません。