Décompilons pour voir ce que GCC 4.8 en fait.
Blagovest a mentionné l'inversion de branche pour améliorer le pipeline, mais les compilateurs actuels le font-ils vraiment ? Nous allons le découvrir !
Sans __builtin_expect
#include "stdio.h"
#include "time.h"
int main() {
/* Use time to prevent it from being optimized away. */
int i = !time(NULL);
if (i)
puts("a");
return 0;
}
Compiler et décompiler avec GCC 4.8.2 x86_64 Linux :
gcc -c -O3 -std=gnu11 main.c
objdump -dr main.o
Sortie :
0000000000000000 <main>:
0: 48 83 ec 08 sub $0x8,%rsp
4: 31 ff xor %edi,%edi
6: e8 00 00 00 00 callq b <main+0xb>
7: R_X86_64_PC32 time-0x4
b: 48 85 c0 test %rax,%rax
e: 75 0a jne 1a <main+0x1a>
10: bf 00 00 00 00 mov $0x0,%edi
11: R_X86_64_32 .rodata.str1.1
15: e8 00 00 00 00 callq 1a <main+0x1a>
16: R_X86_64_PC32 puts-0x4
1a: 31 c0 xor %eax,%eax
1c: 48 83 c4 08 add $0x8,%rsp
20: c3 retq
L'ordre des instructions en mémoire était inchangé : d'abord l'instruction puts
et ensuite retq
retour.
Avec __builtin_expect
Maintenant, remplacez if (i)
avec :
if (__builtin_expect(i, 0))
et on obtient :
0000000000000000 <main>:
0: 48 83 ec 08 sub $0x8,%rsp
4: 31 ff xor %edi,%edi
6: e8 00 00 00 00 callq b <main+0xb>
7: R_X86_64_PC32 time-0x4
b: 48 85 c0 test %rax,%rax
e: 74 07 je 17 <main+0x17>
10: 31 c0 xor %eax,%eax
12: 48 83 c4 08 add $0x8,%rsp
16: c3 retq
17: bf 00 00 00 00 mov $0x0,%edi
18: R_X86_64_32 .rodata.str1.1
1c: e8 00 00 00 00 callq 21 <main+0x21>
1d: R_X86_64_PC32 puts-0x4
21: eb ed jmp 10 <main+0x10>
El puts
a été déplacée à la toute fin de la fonction, la fonction retq
retour !
Le nouveau code est essentiellement le même :
int i = !time(NULL);
if (i)
goto puts;
ret:
return 0;
puts:
puts("a");
goto ret;
Cette optimisation n'a pas été faite avec -O0
.
Mais bonne chance pour écrire un exemple qui fonctionne plus rapidement avec __builtin_expect
que sans, Les processeurs sont vraiment intelligents de nos jours. . Mes tentatives naïves sont ici .
C++20 [[likely]]
y [[unlikely]]
C++20 a normalisé ces modules intégrés C++ : Comment utiliser l'attribut probable/non probable de C++20 dans une instruction if-else ? Ils feront probablement (un jeu de mots !) la même chose.
2 votes
Duplicata possible de Macros likely()/unlikely() dans le noyau Linux - comment fonctionnent-elles ? Quels sont leurs avantages ?
5 votes
Je pense que votre direct Le code aurait dû être
if ( x == 0) {} else foo();
ou simplementif ( x != 0 ) foo();
qui est équivalent au code de la documentation GCC.