Pour les architectures Intel, existe-t-il un moyen de demander au compilateur GCC de générer du code qui force toujours la prédiction de branche d'une manière particulière dans mon code? Le matériel Intel prend-il même en charge cela? Qu'en est-il des autres compilateurs ou matériels?
J'utiliserais ceci dans le code C ++ où je connais le cas où je souhaite courir vite et ne me soucie pas du ralentissement lorsque l'autre branche doit être prise même lorsqu'elle a récemment pris cette branche.
for (;;) {
if (normal) { // How to tell compiler to always branch predict true value?
doSomethingNormal();
} else {
exceptionalCase();
}
}
En tant que question suivante pour Evdzhan Mustafa, l'indice peut-il simplement spécifier un indice pour la première fois que le processeur rencontre l'instruction, toutes les prédictions de branche ultérieures, fonctionnant normalement?
Réponses:
À partir de C ++ 20, les attributs probables et improbables doivent être normalisés et sont déjà pris en charge dans g ++ 9 . Ainsi, comme discuté ici , vous pouvez écrire
par exemple, dans le code suivant, le bloc else est inséré grâce au
[[unlikely]]
bloc iflien godbolt comparant la présence / absence de l'attribut
la source
[[unlikely]]
dansif
vs[[likely]]
dans leelse
?GCC prend en charge la fonction
__builtin_expect(long exp, long c)
de fournir ce type de fonctionnalité. Vous pouvez consulter la documentation ici .Où
exp
est la condition utilisée etc
est la valeur attendue. Par exemple, dans votre cas, vous voudriezEn raison de la syntaxe peu pratique, cela est généralement utilisé en définissant deux macros personnalisées comme
juste pour faciliter la tâche.
Faites attention à cela:
la source
constexpr
fonction?constexpr
fonction puisse remplacer cette macro. Cela doit figurerif
directement dans la déclaration, je crois. La même raisonassert
ne pourrait jamais être uneconstexpr
fonction.constexpr
ne parle que de la sémantique de valeur, et non l'inline de réunion spécifique à la mise en œuvre); l'interprétation directe (pas en ligne) du code n'a pas de sens. Il n'y a aucune raison d'utiliser une fonction pour cela.__builtin_expect
lui - même est un indice d'optimisation, donc affirmer qu'une méthode simplifiant son utilisation dépend de l'optimisation n'est ... pas convaincant. De plus, je n'ai pas ajouté leconstexpr
spécificateur pour le faire fonctionner en premier lieu, mais pour le faire fonctionner dans des expressions constantes. Et oui, il y a des raisons d'utiliser une fonction. Par exemple, je ne voudrais pas polluer tout mon espace de noms avec un joli petit nom tel quelikely
. Je devrais utiliser par exempleLIKELY
, pour souligner que c'est une macro et éviter les collisions, mais c'est tout simplement moche.gcc a long __builtin_expect (long exp, long c) (c'est moi qui souligne ):
Comme le note la documentation, vous devriez préférer utiliser les commentaires de profil réels et cet article en montre un exemple pratique et comment cela, dans leur cas, finit au moins par être une amélioration par rapport à l'utilisation
__builtin_expect
. Voir également Comment utiliser les optimisations guidées par profil dans g ++? .On peut également trouver un article pour les débutants du noyau Linux sur les macros noyau probable () et improbable () qui utilisent cette fonctionnalité:
Notez le
!!
utilisé dans la macro, nous pouvons trouver l'explication pour cela dans Pourquoi utiliser !! (condition) au lieu de (condition)? .Ce n'est pas parce que cette technique est utilisée dans le noyau Linux qu'il est toujours logique de l'utiliser. Nous pouvons voir à partir de cette question que j'ai récemment répondu à la différence entre les performances de la fonction lors du passage du paramètre en tant que constante de temps de compilation ou variable que de nombreuses techniques d'optimisation roulées à la main ne fonctionnent pas dans le cas général. Nous devons profiler soigneusement le code pour comprendre si une technique est efficace. De nombreuses techniques anciennes peuvent même ne pas être pertinentes avec les optimisations modernes du compilateur.
Remarque, bien que les fonctions intégrées ne soient pas portables clang prend également en charge __builtin_expect .
De plus, sur certaines architectures, cela peut ne pas faire de différence .
la source
Non, il n'y en a pas. (Au moins sur les processeurs x86 modernes.)
__builtin_expect
mentionné dans d'autres réponses influence la manière dont gcc organise le code d'assemblage. Il n'influence pas directement le prédicteur de branche du CPU.Bien sûr, il y aura des effets indirects sur la prédiction de branche causés par la réorganisation du code. Mais sur les processeurs x86 modernes, il n'y a aucune instruction qui dit au CPU "suppose que cette branche est / n'est pas prise".Voir cette question pour plus de détails: Intel x86 0x2E / 0x3E Prefix Branch Prediction réellement utilisé?
Pour être clair,
__builtin_expect
et / ou l'utilisation de-fprofile-arcs
peut améliorer les performances de votre code, à la fois en donnant des conseils au prédicteur de branche via la disposition du code (voir Optimisations des performances de l'assemblage x86-64 - Alignement et prédiction de branche ), et en améliorant également le comportement du cache en gardant le code "improbable" loin du code "probable".la source
__builtin_expect
.__builtin_expect
. Cela devrait donc être juste un commentaire. Mais ce n'est pas faux, j'ai donc supprimé mon vote défavorable.__builtin_expect
pour créer de manière triviale un cas de test avec lequel vous pouvez mesurer etperf stat
qui aura un taux d'erreur de prédiction de branche très élevé. Cela affecte simplement la disposition des branches . Et BTW, Intel depuis Sandybridge ou du moins Haswell n'utilise pas beaucoup / pas du tout la prédiction statique; il y a toujours une prédiction dans le BHT, que ce soit un alias périmé ou non. xania.org/201602/bpu-part-twoLa manière correcte de définir des macros probables / improbables dans C ++ 11 est la suivante:
Cette méthode est compatible avec toutes les versions de C ++, contrairement à
[[likely]]
, mais repose sur une extension non standard__builtin_expect
.Lorsque ces macros sont définies de cette façon:
Cela peut changer la signification des
if
instructions et casser le code. Considérez le code suivant:Et sa sortie:
Comme vous pouvez le voir, la définition de LIKELY en utilisant
!!
comme un cast pourbool
casser la sémantique deif
.Le point ici n'est pas cela
operator int()
etoperator bool()
devrait être lié. Ce qui est une bonne pratique.Plutôt que d'utiliser
!!(x)
au lieu destatic_cast<bool>(x)
perd le contexte pour les conversions contextuelles C ++ 11 .la source
switch
, merci. La conversion contextuelle impliquée ici est partucléaire du typebool
et des cinq contextes spécifiques qui y sont répertoriés , qui n'incluent pas leswitch
contexte.(_Bool)(condition)
, car C n'a pas de surcharge d'opérateurs.(condition)
, non!!(condition)
. Les deux sonttrue
après avoir changé cela (testé avec g ++ 7.1). Pouvez-vous construire un exemple qui démontre réellement le problème dont vous parlez lorsque vous utilisez!!
pour booleanize?Comme les autres réponses l'ont toutes suggérées de manière adéquate, vous pouvez utiliser
__builtin_expect
pour donner au compilateur un indice sur la façon d'organiser le code d'assembly. Comme le soulignent les documents officiels , dans la plupart des cas, l'assembleur intégré à votre cerveau ne sera pas aussi bon que celui conçu par l'équipe GCC. Il est toujours préférable d'utiliser les données de profil réelles pour optimiser votre code, plutôt que de deviner.Dans le même ordre d'idées, mais pas encore mentionné, il y a une manière spécifique à GCC de forcer le compilateur à générer du code sur un chemin "froid". Cela implique l'utilisation des attributs
noinline
etcold
, qui font exactement ce qu'ils semblent faire. Ces attributs ne peuvent être appliqués qu'aux fonctions, mais avec C ++ 11, vous pouvez déclarer des fonctions lambda en ligne et ces deux attributs peuvent également être appliqués aux fonctions lambda.Bien que cela tombe toujours dans la catégorie générale d'une micro-optimisation, et donc le conseil standard s'applique - testez ne pas deviner - je pense que c'est plus généralement utile que
__builtin_expect
. Presque toutes les générations du processeur x86 utilisent des indices de prédiction de branche ( référence ), donc la seule chose que vous allez pouvoir affecter de toute façon est l'ordre du code d'assemblage. Puisque vous savez ce qu'est la gestion des erreurs ou le code de "cas de bord", vous pouvez utiliser cette annotation pour vous assurer que le compilateur ne prédira jamais une branche vers celui-ci et le liera loin du code "chaud" lors de l'optimisation de la taille.Exemple d'utilisation:
Mieux encore, GCC l'ignorera automatiquement en faveur des commentaires de profil lorsqu'il est disponible (par exemple, lors de la compilation avec
-fprofile-use
).Voir la documentation officielle ici: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
la source
__builtin_expect
fait. Ce n'est pas du tout inutile. Vous avez raison de dire que l'cold
attribut est également utile, mais vous sous-estimez l'utilité de__builtin_expect
je pense.__builtin_expect peut être utilisé pour indiquer au compilateur dans quelle direction vous attendez une branche. Cela peut influencer la manière dont le code est généré. Les processeurs typiques exécutent le code plus rapidement de manière séquentielle. Donc si tu écris
le compilateur générera du code comme
Si votre indice est correct, cela exécutera le code sans aucune branche réellement effectuée. Il s'exécutera plus rapidement que la séquence normale, où chaque instruction if se ramifierait autour du code conditionnel et exécuterait trois branches.
Les nouveaux processeurs x86 ont des instructions pour les branches qui devraient être prises, ou pour les branches qui ne devraient pas l'être (il y a un préfixe d'instruction; pas sûr des détails). Je ne sais pas si le processeur l'utilise. Ce n'est pas très utile, car la prédiction de branche gérera cela très bien. Je ne pense donc pas que vous puissiez réellement influencer la prédiction de branche .
la source
En ce qui concerne l'OP, non, il n'y a aucun moyen dans GCC de dire au processeur de toujours supposer que la branche est ou n'est pas prise. Ce que vous avez, c'est __builtin_expect, qui fait ce que les autres disent. De plus, je pense que vous ne voulez pas dire au processeur si la branche est prise ou pas toujours . Les processeurs actuels, tels que l'architecture Intel, peuvent reconnaître des modèles assez complexes et s'adapter efficacement.
Cependant, vous souhaitez parfois contrôler si par défaut une branche est prédite prise ou non: Lorsque vous savez que le code sera appelé "froid" en ce qui concerne les statistiques de branchement.
Un exemple concret: le code de gestion des exceptions. Par définition, le code de gestion se produira exceptionnellement, mais peut-être que lorsqu'il se produira, des performances maximales sont souhaitées (il peut y avoir une erreur critique à prendre en charge dès que possible), par conséquent, vous voudrez peut-être contrôler la prédiction par défaut.
Un autre exemple: vous pouvez classer votre entrée et sauter dans le code qui gère le résultat de votre classification. S'il existe de nombreuses classifications, le processeur peut collecter des statistiques mais les perdre car la même classification ne se produit pas assez tôt et les ressources de prédiction sont consacrées au code récemment appelé. Je souhaite qu'il y ait une primitive pour dire au processeur "s'il vous plaît ne pas consacrer de ressources de prédiction à ce code" comme vous pouvez parfois dire "ne pas mettre en cache".
la source