Quelle est la différence entre les appels probables et improbables dans le noyau?

11

Quelle est la distance entre les appels probables et improbables dans le noyau. En cherchant dans la source du noyau, j'ai trouvé ces déclarations.

# define likely(x)      __builtin_expect(!!(x), 1)
# define unlikely(x)    __builtin_expect(!!(x), 0)

Quelqu'un pourrait-il y jeter un peu de lumière?

Sen
la source
C'est vraiment une question de programmation, mieux adaptée à Stack OVerflow .
Gilles 'SO- arrête d'être méchant'
stackoverflow.com/questions/109710/…
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Réponses:

14

Ce sont des conseils de compilation pour GCC. Ils sont utilisés dans les conditions pour indiquer au compilateur si une branche est susceptible d'être prise ou non. Cela peut aider le compilateur à définir le code de manière optimale pour le résultat le plus fréquent.

Ils sont utilisés comme ceci:

if (likely(some_condition)) {
  // the compiler will try and make the code layout optimal for the case
  // where some_condition is true, i.e. where this block is run
  most_likely_action();
} else {
  // this block is less frequently used
  corner_case();
}

Il doit être utilisé avec le plus grand soin (c'est-à-dire sur la base des résultats de profilage de branche réels). Un mauvais indice peut dégrader les performances (évidemment).

Quelques exemples de la façon dont le code peut être optimisé sont facilement trouvés en recherchant GCC __builtin_expect. Cet article de blog optimisation gcc: __builtin_expect détaille par exemple un démontage avec celui-ci.

Le type d'optimisations qui peut être effectué est très spécifique au processeur. L'idée générale est que, souvent, les processeurs exécutent le code plus rapidement s'il ne se branche / saute pas partout. Plus elle est linéaire et plus les branches sont prévisibles, plus elle s'exécutera rapidement. (Cela est particulièrement vrai pour les processeurs avec des pipelines profonds par exemple.)

Ainsi, le compilateur émettra le code de telle sorte que la branche la plus probable n'impliquera pas de saut si c'est ce que le CPU cible préfère, par exemple.

Tapis
la source
Qu'entend-on par licornes ? S'agit-il d'un terme technique ou simplement d'un remplissage?
Sen
J'ai retiré les licornes pour éviter toute confusion.
Mat
Pourriez-vous s'il vous plaît élaborer sur le compilateur va essayer de rendre la disposition du code optimale pour le cas ? J'aimerais savoir comment ça se passe.
Sen
ajouté un peu d'informations à ce sujet. il n'y a pas de moyen général d'optimiser le code, tout dépend du processeur.
Mat
2

Décompilons pour voir ce que GCC 4.8 en fait

Sans attendre

#include "stdio.h"
#include "time.h"

int main() {
    /* Use time to prevent it from being optimized away. */
    int i = !time(NULL);
    if (i)
        printf("%d\n", i);
    puts("a");
    return 0;
}

Compilez et décompilez avec GCC 4.8.2 x86_64 Linux:

gcc -c -O3 -std=gnu11 main.c
objdump -dr main.o

Production:

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 14                   jne    24 <main+0x24>
  10:       ba 01 00 00 00          mov    $0x1,%edx
  15:       be 00 00 00 00          mov    $0x0,%esi
                    16: R_X86_64_32 .rodata.str1.1
  1a:       bf 01 00 00 00          mov    $0x1,%edi
  1f:       e8 00 00 00 00          callq  24 <main+0x24>
                    20: R_X86_64_PC32       __printf_chk-0x4
  24:       bf 00 00 00 00          mov    $0x0,%edi
                    25: R_X86_64_32 .rodata.str1.1+0x4
  29:       e8 00 00 00 00          callq  2e <main+0x2e>
                    2a: R_X86_64_PC32       puts-0x4
  2e:       31 c0                   xor    %eax,%eax
  30:       48 83 c4 08             add    $0x8,%rsp
  34:       c3                      retq

L'ordre des instructions en mémoire est resté inchangé: d'abord le printf, puis putsle retqretour.

Avec attendre

Remplacez maintenant if (i)par:

if (__builtin_expect(i, 0))

et nous obtenons:

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 11                   je     21 <main+0x21>
  10:       bf 00 00 00 00          mov    $0x0,%edi
                    11: R_X86_64_32 .rodata.str1.1+0x4
  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
  21:       ba 01 00 00 00          mov    $0x1,%edx
  26:       be 00 00 00 00          mov    $0x0,%esi
                    27: R_X86_64_32 .rodata.str1.1
  2b:       bf 01 00 00 00          mov    $0x1,%edi
  30:       e8 00 00 00 00          callq  35 <main+0x35>
                    31: R_X86_64_PC32       __printf_chk-0x4
  35:       eb d9                   jmp    10 <main+0x10>

Le printf(compilé en __printf_chk) a été déplacé à la toute fin de la fonction, après putset le retour pour améliorer la prédiction de branche comme mentionné par d'autres réponses.

C'est donc essentiellement la même chose que:

int i = !time(NULL);
if (i)
    goto printf;
puts:
puts("a");
return 0;
printf:
printf("%d\n", i);
goto puts;

Cette optimisation n'a pas été effectuée avec -O0.

Mais bonne chance pour écrire un exemple qui tourne plus vite avec __builtin_expectque sans, les processeurs sont vraiment intelligents de nos jours . Mes tentatives naïves sont là .

C ++ 20 [[likely]]et[[unlikely]]

C ++ 20 a normalisé ces éléments intégrés C ++: /programming/51797959/how-to-use-c20s-l probable -unlikely-attribute-in-if-else- statement Ils le feront probablement (un jeu de mots!) faire la même chose.

Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
la source