J'ai un comparer () code> fonction qui ressemble à ceci:
_Z7Comparebii:
.LFB4:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movl %edi, %eax
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movb %al, -4(%rbp)
cmpb $0, -4(%rbp)
je .L2
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setge %al
jmp .L3
.L2:
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setle %al
.L3:
leave
ret
.cfi_endproc
.LFE4:
.size _Z7Comparebii, .-_Z7Comparebii
.section .text._Z8Compare2bii,"axG",@progbits,_Z8Compare2bii,comdat
.weak _Z8Compare2bii
.type _Z8Compare2bii, @function
_Z8Compare2bii:
.LFB5:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movl %edi, %eax
movl %esi, -24(%rbp)
movl %edx, -28(%rbp)
movb %al, -20(%rbp)
movw $0, -16(%rbp)
movl -24(%rbp), %eax
cmpl -28(%rbp), %eax
setle %al
movb %al, -16(%rbp)
movl -24(%rbp), %eax
cmpl -28(%rbp), %eax
setge %al
movb %al, -15(%rbp)
movzbl -20(%rbp), %eax
cltq
movzbl -16(%rbp,%rax), %eax
leave
ret
.cfi_endproc
.LFE5:
.size _Z8Compare2bii, .-_Z8Compare2bii
.text
3 Réponses :
Que diriez-vous ...
inline bool Compare4(bool greater, int p1, int p2) { return (greater ^ (p1 <= p2)) | (p1 == p2); }
Il me semble que compare3 (true, 1,1)! = Compare3 (false, 1,1) code>, qui rendrait la fonction incorrecte. Idem pour
compare4 () code>.
Ajouter | (P1 == p2) code> et être heureux.
Hmm, je n'ai pas testé le code. Pas de compilateur dans ma machine à domicile. Va vérifier maintenant.
Bon sang, j'ai raté cette condition. Corrigé-le maintenant. Merci.
On dirait que Compare4 est le plus rapide.
En fait, par mes tests, je vois que la comparaison2 est la plus rapide. Je reçois la comparaison2 à environ 1,65 n et comparez4 à environ 2,55nes.
Cela ne traite pas vraiment de la question (c'est-à-dire "pourquoi la différence entre comparer () et comparer2 ()?")
@Olicharlesworth c'est un euphémisme;)
Je pense que je pensais la majeure partie de celle-ci.
Lorsque j'ai posté l'assemblage pour les fonctions de mon op edit, j'ai noté que la version inlinette pourrait être différente. Je n'avais pas examiné ni posté le code temporel car c'était poilu, et parce que je pensais que le processus d'inlinage ne changerait pas si la ramification a lieu ou non dans puis, lorsque j'ai remplacé comparer () code>. P> Lorsque je n'ai pas induit la fonction et répété mes mesures, j'ai eu les résultats suivants: p>
a [i] = rand ( )% 2 code> avec
A [i] = false code>, j'ai eu les suivants: p>
Compare(): 2.59ns avg
Compare2(): 3.16ns avg
J'ai écrit une bibliothèque C ++ appelée Celerero conçu pour tester de telles optimisations et alternatives. (Auto sans vergogne Promotion: https://github.com/digitalinblue/celero )
J'ai couru votre Coques utilisant le code suivant: p> Les résultats sont présentés ci-dessous: p> étant donné ce test, il ressemble à Compare2 Strort> est la meilleure option de cette micro-optimisation. p> EDIT: P> assemblage2 (le meilleur cas): p> ASSEMBLAGE3 ASSEMBLAGE (le meilleur des boîtiers suivants): P> xor r11d, r11d
cmp r8d, r9d
mov r10d, r11d
setg r10b
test dl, dl
mov ecx, r11d
sete cl
mov eax, r11d
cmp ecx, r10d
setne al
cmp r8d, r9d
sete r11b
or eax, r11d
Intéressant, mais ici, nous voulons savoir pourquoi i> c'est.
J'ai ajouté l'assemblée à ma réponse.
Je ne suis pas un fan de la façon dont vous avez fait benchmarking. Les temps mesurés sont dominés par le coût de rand () code>, masquant la véritable différence de performances entre les variantes.
C'est vrai que Rand () est coûteux, mais le coût est identique pour chaque test, il peut donc être pris en compte. Ce qui devrait être comparé est une heure de base (relative). Cela montre ce qui est vraiment plus rapide et de combien. La mesure de l'heure d'exécution moyenne est en fait incorrecte. Référence: codeproject.com/articles/525576/... < / a>
Compte tenu de la base de référence, la comparaison2 est de 1,014870 fois plus lente que la mesure de base et la comparaison3 est de 1,027476 fois plus lente.
Oh je vois. Je suppose que j'aurais juste vu la déclaration "Comparer2 est de 1,85x plus rapide que le comparateur3" (depuis (1.027476-1) / (1.014870-1) = 1,85). La façon dont vos chiffres sont rapportées rend l'ampleur de l'amélioration non évidente, à mon avis.
Optimiser pour éviter la prédiction des succursales Code> N'est-ce pas un oxymoron?
Vous devrez partager le code de montage depuis ce qui se passe dépend beaucoup de quel compilateur que vous utilisez et à quel niveau d'optimisation.
@ Last Line: Alors pourquoi ne postez-vous pas l'assemblage?
Vous n'avez pas mis la graine. Peut-être que le compilateur est assez intelligent de savoir ce que
rand () code> revient dans ce cas? Juste une pensée rapide. Aussi, vous devriez vraiment comparer l'Assemblée. Même si vous êtes de l'Assemblée-Code-analphabète, vous pouvez toujours montrer la différence.
Aurait pu être un mouvement conditionnel .. montrer l'assemblage.
Il est difficile de dire sans voir ce que fait le compilateur.
Quelle est votre arc et votre compilateur?
Quel est le timing pour un [i] = faux? Plus rapide que le 1.61NS?
@Lukarahne: J'utilise g ++ 4.4.3-4ubuntu5 sur un Intel Xeon X5570. @ Autres: supporte avec moi pendant que je découvre comment poster l'assemblée.
@Antonroth Comparer () et Comparez2 () Horcier à ~ 3.14ns et ~ 1.61NS, respectivement, dans les 3 cas [I]: rand (), vrai et faux.
Attends, ce qui est plus rapide? Votre dernier commentaire vient de dire que
comparer () code> est plus rapide, mais vous en avez l'inverse dans votre question.
Ne vous inquiétez pas de répondre à mon commentaire.
Juste pour confirmer, est-ce compilé avec
-O3 code>?
BTW Ce que vous voulez éviter est les branches, pas la prédiction de la branche - la prédiction des succursales est un bonne i> chose.
@Mysticia compare2 () est plus rapide, j'ai tapé mon commentaire à Antonroth à la hâte et ensuite la éditer rapidement par la suite.
@Olicharlesworth oui, avec -O3
@MarkRansom: Sauf si cela ne prédit pas de manière incorrecte un pourcentage important du temps ...
Auquel cas, le terme serait "éviter les délais de branche", qui peut également être fait en évitant complètement les branches.
Oui. Dans ce cas, la prédiction des succursales sera fausse un pourcentage important du temps.
@Olicharlesworth, même s'il s'agissait de 100% du temps, ce ne serait pas pire que de caler le pipeline le ferait-il? Et de manière réaliste, cela ne devrait pas être faux de plus de 50%.
La question posée dans le premier commentaire sur cette question, heureusement ignorée par tout le monde, est «est une mauvaise prédiction que pas de prédiction»?
@MarkRansom Eh bien, si vous comparez deux processeurs à proximité identiques: une avec et une avec une prédiction sans succursale, il est probable que celui-ci n'est probablement pas plus rapide que celui avec celui-ci et mal prédit 100% du temps. C'est parce que le nettoyage de mauvaise réputation a des frais généraux importants.
@David Notez le
SEEGE CODE> et
SETLE CODE> INSTRUCTIONS? Ils fournissent un résultat booléen d'une comparaison sans faire de branches. Les deux versions utilisent ces instructions. Ne répond pas à votre question mais je pensais que vous le trouveriez intéressant.
Pourriez-vous essayer
(rand () / 256)% 2 code> au lieu de
rand ()% 2 code>? Le moins important de rand () peut être bien prévisible et tout aussi bon que
true code> ou
false code> pour le prédicteur de la branche.
@MarkRansom: C'est vrai, pour des données aléatoires, on pourrait s'attendre à ce que 50% de taux de répression (je pense). Il faudrait qu'une contribution pathologique spécifique pour provoquer une hypothèse de 100%.
J'ai essayé
(rand ()% 256) <128 code> au lieu de
rand ()% 2 code> même résultat.
Il est facile de dire pourquoi les deux fonctions fonctionnent également pour un premier paramètre aléatoire / constant: aucun d'entre eux n'utilise des instructions de branche (lorsqu'elles sont inlinées / optimisées). Mais je ne sais pas pourquoi
compare2 code> est plus rapide. En fait, la seule différence est que
comparer code> utilise
cmov code> instruction tandis que
compare2 code> utilise deux fois plus d'accès à la mémoire. Je m'attendrais à ce que
compare2 code> soit deux fois plus lent que
comparer code> ...
@EvGeny Il y a clairement une succursale dans
Comparer () Code> - au moins lorsqu'il n'est pas inliné (et je ne vois pas pourquoi l'allusion aiderait si le compilateur ne peut pas déduire la valeur de plus en plus). G ++ 4.5.3 Sous Cygwin crée le code attendu pour moi sans surprises (trop de coups de mouvements dans l'assemblée donnée pour moi)