26

I used both of these compilers in different projects.

How they are different in terms of code processing and output generations? For example both gcc and clang has -O2 options for optimization. Are they operating the same way (high level) in terms of optimizing code? I did a little test , for example if I have the following code:

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

the following are the output assemblies with clang and gcc with -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

as it can be seen the output has different instructions. So my question is does one of them has advantage over another in different projects?

nalzok
  • 14,965
  • 21
  • 72
  • 139
Pooya
  • 6,083
  • 3
  • 23
  • 43
  • 3
    You could improve your code by writing `int foo(int num) { return num * num + ~num & 1; }` instead. – fuz Apr 11 '16 at 08:24
  • 1
    @FUZxxl: great point, that [makes much better code](https://godbolt.org/g/Y1RZuj), but you need parens `int foo(int num) { return num * num + (~num & 1); }` because `&` has lower precedence than * and +. Also, that does have different behaviour for negative numbers. In C, `-1 % 2` is `-1`, so the if is false. gcc's code accounts for this if you write `n*n + (n%2)`. – Peter Cordes Apr 11 '16 at 08:29
  • @FUZxxl thanks for the note, it is just a simple test to see which compilers output what – Pooya Apr 11 '16 at 08:36
  • @PeterCordes Right. You are of course correct. – fuz Apr 11 '16 at 08:45
  • 5
    @FUZxxl `return num * num + ~num & 1;` are you kidding me? That code is confusing, you made a mistake in a single line, original code is safer for larger pool of people to maintain. – user1135541 Apr 12 '16 at 20:04

2 Answers2

25

Yes. And no.

This is like asking whether an Audi car has an advantage over a Mercedes car. Like them, the two compilers are two different projects aiming to do the same thing. In some cases, gcc will emit better code, in others it will be clang.

When you need to know, you have to compile your code with both and then measure it.

There is an argument here and somewhat less related here.

Community
  • 1
  • 1
Honza Remeš
  • 1,193
  • 8
  • 11
17

In this case the Clang output is better, because it does not branch; instead it loads the value of num % 2 == 1 to al the code generated by gcc uses jumps. If num is expected to be even/odd with 50 % chances, and with no repeating patterns, the code generated by GCC will be susceptible to branch prediction failure.


However you can make the code well-behaved on GCC as well by doing

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

Even more so, as it seems that your algorithm is really defined for unsigned numbers only, you should use unsigned int (they're different for negative numbers) - actually you get a major speedup by using unsigned int for the argument, as now GCC/Clang can optimize num % 2 to num & 1:

unsigned int foo(unsigned int num) {
    return num * num + (num % 2 != 1);
}

The resulting code generated by gcc -O2

movl    %edi, %edx
imull   %edi, %edi
andl    $1, %edx
xorl    $1, %edx
leal    (%rdi,%rdx), %eax
ret

is much better than the code for your original function generated by either compiler. Thus a compiler does not matter as much as does a programmer who knows what he's doing.

Community
  • 1
  • 1