Pour un compilateur moderne typique sur le matériel moderne, le En d'autres termes, appelant les deux cas pour éviter une branche possible: p> sera-t-il? : Code> L'opérateur aboutit à une branche qui affecte le pipeline d'instructions?
bool testVar = someValue();
purge(testVar ? white : black);
5 Réponses :
Je ne peux pas imaginer que la première méthode serait plus rapide. p>
Avec la première méthode, vous pouvez éviter une branche, mais vous le remplacez par un appel de fonction, qui impliquerait généralement une branche plus une succursale plus (à moins que cela n'ait été inliné). Même s'il est inliné, à moins que la fonctionnalité à l'intérieur de la fonction de purge () n'était absolument triviale, il serait presque certainement plus lent. P>
Pas nécessairement. Cette branche inconditionnelle ne nécessitera pas le pipeline à rougir. Si la fonction est relativement simple, elle peut i> être globalement moins chère.
appeler une fonction est au moins aussi coûteux que le test logique + saut (et oui, le ?: code> opérateur ternaire nécessiterait un saut). P>
En supposant que cela se produise fréquemment à l'intérieur d'une boucle, l'appel de la fonction inconditionnelle n'entraînera pas une faute de répugnance de la branche, et ne nécessitera donc pas que le pipeline de la CPU soit rincé. Le test logique peut impliquer un saut conditionnel; Ce sera beaucoup plus cher que le saut inconditionnel.
Dans le premier cas, la purge est appelée deux fois. Dans le second cas, la purge est appelée une fois p>
Il est difficile de répondre à la question à propos de la ramification, car elle dépend donc des compilateurs et des instructions. Par exemple sur un bras (qui a une exécution d'instructions conditionnelle), il pourrait ne pas agir. Sur un X86, il sera presque certainement p>
dépend de la plate-forme. Plus précisément, cela dépend de la taille du tableau de prévision de saut de la CPU et de savoir si la CPU permet des opérations conditionnelles (comme sur le bras). P>
Les CPU avec des opérations conditionnelles favoriseront fortement le deuxième cas. Les processeurs avec des tables de prévision de saut plus gros favoriseront le premier cas. P>
La vraie réponse (comme avec toutes les autres questions de performance): Mesurer et comparer. Parfois, le reste du code jette une boule de courbe et il est généralement impossible de prédire les effets de certains changements. P>
La plupart des ISA modernes ont une forme de sélectivité conditionnelle, comme x86-64 cmov code>, Aarch64
CSEL code>, MIPS
MOVZ code>. Pour les cas où les deux options à sélectionner ne nécessitent aucun calcul supplémentaire, la plupart des compilateurs compileront un simple ternaire à cela. (Sur certaines ISA, comme un bras 32 bits et je pense que Itanium, il est en fait possible d'éviter de caler sur des charges de cache-manquées dont la valeur ne sera pas nécessaire sans ramification, ce qui le rend encore mieux à faire sans succursale. Sur le bras via le conditionnel. Charges, ou sur Itanium via des charges spéculatives, je pense.)
Même sans instruction, comme CMOV code>, un bitpack peut transformer un booléen en un masque 0 / 0xFFFFFFF qui peut être utilisé avec et / ou pour mettre en œuvre la même opération. Sur quelques processeurs, un compilateur peut même choisir de le faire, en particulier pour des opérations telles que le complément de 2
ABS () code> où un bitpack sans succursale juste pour cela n'est que quelques instructions.
L'instruction CMOV (mouvement conditionnel) fait partie de l'ensemble d'instructions X86 depuis le Pentium Pro. Il est rarement généré automatiquement par GCC en raison des options du compilateur couramment utilisées et des restrictions placées par le langage C. Une séquence SETCC / CMOV peut être insérée par ensemble en ligne dans votre programme C. Cela ne devrait être fait que des cas où la variable conditionnelle est une valeur oscillante de manière aléatoire dans la boucle interne (des millions d'exécutions) d'un programme. Dans des cas non oscillants et dans les cas de modèles d'oscillation simples, les processeurs modernes peuvent prédire des succursales avec un degré de précision très élevé. En 2007, Linus Torvalds suggéré ici a > Pour éviter l'utilisation de CMOV dans la plupart des situations. P>
Intel décrit le déplacement conditionnel dans le Manuel du développeur de logiciels d'architecture Intel (R) , Volume 2: Manuel de référence du jeu d'instructions : P>
Les instructions CMOVCC vérifient l'état d'un ou plusieurs de l'état
drapeaux dans les eFlags Inscrivez-vous (CF, de, PF, SF et ZF) et effectuez un
Déplacer le fonctionnement si les indicateurs sont dans un état (ou une condition) spécifié. UNE
Le code de condition (CC) est associé à chaque instruction pour indiquer
la condition testée pour. Si la condition n'est pas satisfaite, un
mouvement n'est pas effectué et l'exécution continue avec l'instruction
Suite à l'instruction CMOVCC. P>
Ces instructions peuvent déplacer une valeur de 16 ou 32 bits de la mémoire à un.
registre général ou d'un registre général à usage général de
un autre. Les mouvements conditionnels d'opérandes de registres de 8 bits ne sont pas
pris en charge. p>
Les conditions de chaque mnémonique CMOVCC sont données dans la description
colonne de la table ci-dessus. Les termes «moins» et «plus grand» sont utilisés pour
les comparaisons des entiers signés et les termes "ci-dessus" et "ci-dessous" sont
utilisé pour les entiers non signés. P>
Parce qu'un état particulier des drapeaux d'état peut parfois être
Interprété de deux manières, deux mnémoniques sont définies pour certains opcodes.
Par exemple, le CMOVA (mouvement conditionnel si ci-dessus) l'instruction et le
CMOVNBE (déplacement conditionnel s'il n'est pas inférieur ou égal) instruction est
Alterner Mnemonics pour l'opcode 0F 47H. P>
blockQuote>
Je suis à peu près sûr que l'appel de la fonction sera beaucoup plus cher que la ramification, mais je ne sais pas assez pour le remettre et le poster comme une réponse. Pourquoi n'essayez-vous pas les deux et profilez-vous?
Fyi,
?: code> est connu sous le nom de l'opérateur ternaire i>.
Pourquoi n'essayez-vous pas avec votre compilateur et voyez-vous? Il suffit de regarder le code de montage généré.
@Jared ng - pas tout à fait. Un opérateur ternaire est n'importe quel opérateur avec trois opérandes. La plupart des langues ont un seul opérateur ternaire -
?: Code> - mais certains (quelque part) ont d'autres. Ternaire est similaire à binaire ou unaire.
?: Code> est A i> Opérateur ternaire, mais son nom est le nom Conditionnel i> Opérateur.
Les deux n'ont-ils pas différents comportements? Juste un code pour le comportement dont vous avez besoin.
@Chris hmm ... bonne prise. J'étais trop précis.
@Gman - je suppose (puisque l'OP semble impliquer que l'une ou l'autre option est acceptable) que
purge code> n'est probablement pas nécessaire, c'est un NOP (peut-être pas à l'assemblage, mais dans l'effet).
Je suis secondaire @chrislutz - il suffit de profiler et de le voir pour votre application spécifique. Il y a beaucoup d'astuces que le compilateur et la CPU apportent à la table, de sorte que les êtres humains ne peuvent pas profiler dans leur tête.