2016-04-11 3 views
8

Я использовал оба этих компилятора в разных проектах.Разница между clang и gcc

Как они отличаются с точки зрения обработки кода и выходных поколений? Например, как gcc, так и clang имеет -O2 варианты оптимизации. Они работают одинаково (на высоком уровне) с точки зрения оптимизации кода? Я сделал небольшой тест, например, если у меня есть следующий код:

int foo(int num) { 
    if(num % 2 == 1) 
     return num * num; 
    else 
     return num * num +1; 
} 

являются следующие выходные сборки с лязгом и НКУ с -O2:

----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 

, как это можно видеть на выходе имеет разные инструкции. Поэтому мой вопрос заключается в том, имеет ли одно из них преимущество перед другим в разных проектах?

+2

Вы можете улучшить свой код, написав 'int foo (int num) {return num * num + ~ num & 1;}' вместо этого. – fuz

+1

@FUZxxl: замечательная точка, которая [делает намного лучший код] (https://godbolt.org/g/Y1RZuj), но вам нужно использовать parens 'int foo (int num) {return num * num + (~ num & 1);} 'потому что' & 'имеет более низкий приоритет, чем * и +. Кроме того, это имеет различное поведение для отрицательных чисел. В C, -1% 2'' '-1', поэтому if является ложным. если вы пишете 'n * n + (n% 2)'. –

+0

@FUZxxl спасибо за примечание, это просто простой тест, чтобы увидеть, какие компиляторы выводят то, что – Pooya

ответ

12

Да. И нет.

Это похоже на вопрос, имеет ли автомобиль Audi преимущество над автомобилем Mercedes. Как и они, два компилятора - это два разных проекта, направленных на то же самое. В некоторых случаях gcc испускает лучший код, в других - clang.

Когда вам нужно знать, вы должны скомпилировать свой код с обоими, а затем измерить его.

Существует аргумент here и несколько менее связан here.

10

В этом случае выход Clang лучше, потому что он не ветвится; вместо этого он загружает значение num % 2 == 1 в al код, созданный gcc, использует скачки. Если ожидается, что num будет четным/нечетным с 50% шансами, и без повторных шаблонов код, созданный GCC, будет susceptible to branch prediction failure.


Однако вы можете сделать код хорошо себя на 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 

намного лучше, чем код для исходной функции, порожденной либо компилятором. Таким образом, компилятор не имеет значения, как программист, который знает, что он делает.

+1

Хм, я бы ошибся, должен был быть, конечно, '! = 1', или' == 0' –

Смежные вопросы