J'ai utilisé ces deux compilateurs dans différents projets.
Comment sont-ils différents en termes de traitement de code et de générations de sortie? Par exemple, gcc
et clang
contient -O2
options d'optimisation. Fonctionnent-ils de la même manière (haut niveau) en termes d'optimisation du code? J'ai fait un petit test, par exemple si j'ai le code suivant:
int foo(int num) {
if(num % 2 == 1)
return num * num;
else
return num * num +1;
}
les assemblys de sortie avec clang et gcc avec -O2 sont les suivants:
----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
comme on peut le voir, la sortie a des instructions différentes. Ma question est donc de savoir si l'un d'entre eux a un avantage sur un autre dans différents projets?
Oui. Et non.
C'est comme demander si une voiture Audi a un avantage sur une voiture Mercedes. Comme eux, les deux compilateurs sont deux projets différents visant à faire la même chose. Dans certains cas, gcc
émettra un meilleur code, dans d'autres, ce sera clang
.
Lorsque vous avez besoin de savoir, vous devez compiler votre code avec les deux, puis le mesurer.
Dans ce cas, la sortie Clang est meilleure, car elle ne se branche pas; à la place, il charge la valeur de num % 2 == 1
à al
le code généré par gcc utilise des sauts. Si num
devrait être pair/impair avec 50% de chances et sans répétition, le code généré par GCC sera susceptible d'échec de prédiction de branchement .
Cependant, vous pouvez également faire en sorte que le code se comporte bien sur GCC en faisant
int foo(int num) {
return num * num + (num % 2 != 1);
}
Plus encore, comme il semble que votre algorithme soit vraiment défini uniquement pour les nombres non signés, vous devez utiliser unsigned int
(ils sont différents pour les nombres négatifs) - en fait, vous obtenez une accélération majeure en utilisant unsigned int
pour l'argument, comme maintenant GCC/Clang peut optimiser num % 2
à num & 1
:
unsigned int foo(unsigned int num) {
return num * num + (num % 2 != 1);
}
Le code résultant généré par gcc -O2
movl %edi, %edx
imull %edi, %edi
andl $1, %edx
xorl $1, %edx
leal (%rdi,%rdx), %eax
ret
est bien meilleur que le code de votre fonction d'origine généré par l'un ou l'autre compilateur. Ainsi, un compilateur n'a pas autant d'importance qu'un programmeur qui sait ce qu'il fait.