Quelle est l'utilisation excessive des macros «probable» et «improbable»?

12

Les macros souvent connues sous le nom de likelyet unlikelyaident le compilateur à savoir si un iffichier va généralement être entré ou ignoré. Son utilisation entraîne des améliorations de performances (plutôt mineures).

J'ai commencé à les utiliser récemment et je ne sais pas à quelle fréquence ces conseils devraient être utilisés. Je l'utilise actuellement avec des vérifications d'erreurs if, qui sont généralement marquées comme unlikely. Par exemple:

mem = malloc(size);
if (unlikely(mem == NULL))
  goto exit_no_mem;

Cela semble correct, mais la vérification des erreurs ifse produit assez souvent et, par conséquent, l'utilisation desdites macros.

Ma question est, est - il trop d'avoir likelyet unlikelymacros sur chaque vérification des erreurs if?

Pendant que nous y sommes, quels autres endroits sont-ils souvent utilisés?


Dans mon utilisation actuelle, c'est dans une bibliothèque qui fait une abstraction du sous-système en temps réel, donc les programmes deviendraient portables entre RTAI, QNX et d'autres. Cela dit, la plupart des fonctions sont plutôt petites et appellent directement une ou deux autres fonctions. Beaucoup sont même des static inlinefonctions.

Donc, tout d'abord, ce n'est pas une application que je pourrais profiler. Il n'est pas logique d '«identifier les goulots d'étranglement» car c'est une bibliothèque, pas une application autonome.

Deuxièmement, c'est un peu comme "Je sais que c'est peu probable, autant le dire au compilateur". Je n'essaie pas activement d'optimiser le if.

Shahbaz
la source
7
des relents de micro optimisation pour moi ...
ratchet freak
2
Pour le code d'application, je les ajouterais uniquement si le profilage montrait que ce code est utilisé dans un chemin d'accès à chaud.
CodesInChaos
@james, qui dit simplement likelyet unlikelyexiste et ce qu'ils font. Je n'ai rien trouvé qui suggérerait réellement quand et où il est préférable de les utiliser.
Shahbaz
@Shahbaz "Si la condition est souvent fausse, l'exécution n'est pas linéaire. Il y a un gros morceau de code inutilisé au milieu qui non seulement pollue le L1i en raison de la prélecture, mais cela peut aussi causer des problèmes avec la prédiction de branche. Si le la prédiction de branche est erronée, l'expression conditionnelle peut être très inefficace. " Donc, des boucles serrées où vous voulez vous assurer que les instructions dont vous avez besoin sont dans le cache L1i
James

Réponses:

12

Avez-vous tellement besoin de performances que vous êtes prêt à polluer votre code avec cela? C'est une optimisation mineure.

  • Le code s'exécute-t-il en boucle serrée?
  • Votre application a-t-elle des problèmes de performances?
  • Avez-vous profilé votre application et déterminé que cette boucle particulière coûte beaucoup de temps CPU?

À moins que vous ne puissiez répondre yesà tout ce qui précède, ne vous embêtez pas avec des trucs comme ça.

Modifier: en réponse à la modification. Même lorsque vous ne pouvez pas profiler, vous pouvez généralement estimer les points d'accès. Une fonction d'allocation de mémoire appelée par tout le monde est un bon candidat, d'autant plus qu'elle ne nécessite qu'une seule utilisation de la macro pour fonctionner pour l'ensemble de la bibliothèque.

Java guy
la source
1
Pour être clair, je n'ai pas voté contre. Cependant, votre réponse ne répond pas vraiment à ma question. Essayez-vous de dire que la (un)likelymacro est rarement utilisée et uniquement dans un code extrêmement critique pour les performances? Est-ce une «mauvaise pratique» de l'utiliser souvent ou simplement «inutile»?
Shahbaz
@Shahbaz Cela rend le code moins lisible et l'optimisation des performances peut aller d'un gain trivial à une perte triviale. Ce dernier lorsque l'hypothèse sur la probabilité était soit incorrecte soit a changé pour être incorrecte en raison d'un changement ultérieur dans d'autres parties du code. Si ne doit jamais être utilisé sauf si nécessaire.
Peter
3
@Peter: Bien qu'il soit dommage que la syntaxe ne soit pas plus agréable, les notations sur ce qui est probable ou improbable peuvent fournir des informations utiles aux humains qui lisent du code. Par exemple, quelqu'un qui a vu if (likely(x==2 || x==3)) doOneThing(); else switch(x) { ... }pourrait juger que l'utilisation par le programmeur d'un ifpour les valeurs 2 et 3 n'était pas simplement une conséquence du fait que le programmeur ne savait pas que C pouvait associer deux caseétiquettes à un seul gestionnaire.
supercat
Personne n'a mentionné ce que je considère comme un point critique. Ce n'est pas seulement que le chemin "improbable" se produit moins souvent, il peut être conditionné à ce chemin que vous ne vous souciez pas du tout de la vitesse. Par exemple, un périphérique ne répond plus, vous devez donc le réinitialiser et dormir quand même.
Benjamin Lindqvist
2

Si vous écrivez pour x86 / x64 (et que vous n'utilisez pas de processeurs vieux de 20 ans), le gain de performances de l'utilisation de __builtin_expect () sera négligeable le cas échéant. La raison en est que les processeurs x86 / x64 modernes (mais pas sûrs à 100% d'Atom), ont une prédiction de branche dynamique, donc essentiellement le CPU "apprend" la branche qui est prise le plus souvent. Bien sûr, ces informations ne peuvent être stockées que pour un nombre limité de succursales, cependant, il n'y a que deux cas possibles. Si (a) c'est une branche "fréquemment utilisée", alors votre programme bénéficiera de cette prédiction de branche dynamique, et si (b) c'est une branche "rare", vous ne verrez pas vraiment de performances réalistes affectées par des erreurs de prédiction dans de telles branches rares (20 cycles CPU de mauvaise prévision de branche ne sont pas TROP mauvais si cela se produit une fois dans une lune bleue).

NB: cela n'implique PAS que sur les x86 / x64 modernes, l'importance de la mauvaise prédiction de branche a diminué: toute branche avec 50-50 chance de saut-nojump encourra toujours une pénalité (IIRC 10-20 cycles CPU), donc dans les boucles internes, les branches peuvent doivent encore être évités. C'est seulement l'importance de __builtin_expect () sur x86 / x64 qui a diminué (IIRC, il y a environ 10-15 ans) - principalement à cause de la prédiction de branche dynamique.

NB2: pour les autres plateformes au-delà de x86 / x64, YMMV.

No-Bugs Hare
la source
Eh bien, le compilateur sait à quelle branche le processeur s'attendra à être moins probable. Et il peut faire en sorte que ce soit le plus improbable. Mais le compilateur connaît probablement déjà ce modèle sans la unlikely-notation.
Deduplicator
@Deduplicator: avec la prédiction de branche dynamique, le compilateur ne sait pas quelle branche est la plus probable car le CPU la calcule lors de l'exécution en fonction des exécutions précédentes jusqu'à ce point précis du code.
No-Bugs Hare