Si j'appelle une fonction virtuelle 1000 fois dans une boucle, vais-je souffrir de la surcharge de recherche à la fourche 1000 fois ou une seule fois? p>
7 Réponses :
Si le compilateur peut déduire que l'objet sur lequel vous appelez la fonction virtuelle ne change pas, alors, en théorie em>, il devrait être capable de hisser la recherche de la boucle . P>
Si votre compilateur particulier est effectivement quelque chose que vous ne pouvez savoir que en examinant le code de montage qu'il produit. P>
... ou en profilant. Écrivez un code qui doit faire 1000 recherches et comparer.
Mais que compareriez-vous-tu contre? Vous ne pouvez pas le comparer à une fonction non virtuelle car cela appelle une adresse absolue par opposition à une adresse indirecte. Vous ne pouvez pas non plus la comparer contre le code qui appelle 1000 objets différents, car a) vous devez obtenir les adresses de ces objets de quelque part, ce qui prend du temps supplémentaire, et b) appeler 1000 objets différents est beaucoup moins caché, donc nous s'attendre à ce qu'il soit plus lent de toute façon.
Comparé contre une volatie foo *. Notez que vous devez d'abord vérifier 1000 appels non virtuels pour voir la quantité de surcharge que vous engagez en rechargeant ce code> sur chaque appel. Ensuite, comparez les appels virtuels volatile et non volatile de FOO * SOO * SOIX pour voir la quantité supplémentaire i> surhead la recherche à la fourchette.
Le compilateur peut être capable d'optimiser - par exemple, ce qui suit est (au moins conceptuellement) optimisé optimisé: Cependant, d'autres cas sont plus difficiles: p> < Pré> xxx pré> La même optimisation conceptuelle est applicable, mais beaucoup plus difficile pour le compilateur à voir. P> Bottom Line - Si vous vous souciez vraiment de cela, compilez votre code avec toutes les optimisations activées. et examinez la sortie d'assembleur du compilateur. P> p>
Le compilateur Visual C ++ (au moins par VS 2008) ne met pas en cache des recherches à table. Encore plus intéressant, il n'écrit pas les appels directs vers des méthodes virtuelles où le type statique de l'objet est scellé . Cependant, la surcharge réelle de la recherche de distribution virtuelle est presque toujours négligeable. L'endroit où vous voyez parfois un coup est dans le fait que les appels virtuels en C ++ ne peuvent pas être remplacés par des appels directs comme ils peuvent dans une machine virtuelle gérée. Cela signifie également aucune inlinage pour les appels virtuels. P>
Le seul moyen VRAI d'établir l'impact de votre application utilise un profileur. P>
En ce qui concerne les spécificités de votre question initiale: Si la méthode virtuelle que vous appelez est assez triviale que l'envoi virtuel elle-même entraîne un impact de performance mesurable, cette méthode est suffisamment faible que la tablette restera dans le cache du processeur tout au long de la boucle. Même si les instructions de montage pour tirer le pointeur de la fonction de la machine à livrer sont exécutées 1000 fois, l'impact de la performance sera beaucoup moins que (1000 * temps pour charger une table à fourche à partir de la mémoire système) code>. P>.
Merci beaucoup pour votre réponse et vos commentaires. Je vérifierai que sur GCC quand je reçois un peu de temps.
Je dirais que cela dépend de votre compilateur ainsi que sur le look de la boucle. Optimiser les compilateurs peut faire beaucoup pour vous et si l'appel VF est prévisible, le compilateur peut vous aider. Peut-être que vous pouvez trouver quelque chose sur les optimisations de votre compilateur dans votre documentation de compilateur. P>
Je sais que ça "pourrait", je ne le fais pas si ça "le fait" m'aider.
Je pense que le problème n'est pas une recherche à la fourchette car c'est une opération très rapide, notamment dans une boucle où vous avez toutes les valeurs requises sur le cache (si la boucle n'est pas trop complexe, mais si elle est complexe, la fonction virtuelle n'aurait pas d'impact sur la performance. beaucoup). Le problème est le fait que le compilateur ne peut pas aligner cette fonction dans la compilation. P>
Ceci est particulièrement un problème lorsque la fonction virtuelle est très petite (par exemple, ne renvoyant qu'une seule valeur). L'impact de performance strong> relatif fort> dans ce cas peut être énorme car vous avez besoin d'un appel de la fonction pour simplement retourner une valeur. Si cette fonction peut être inlinée, cela améliorerait beaucoup les performances. P>
Si la fonction virtuelle est la performance qui prend des performances, je ne me soucierais pas vraiment de circonstances. P>
Pour une étude sur la surcharge des appels de fonction virtuelle, je recommande le papier "Le coût direct des appels de fonction virtuelle en C ++" P>
Essayons d'essayer avec g ++ ciblage x86:
$ cat y.cpp struct A { virtual void not_used(int); virtual void f(int); }; void foo(A &a) { for (unsigned i = 0; i < 1000; ++i) a.f(13); } $ $ gcc -S -O3 y.cpp # assembler output, max optimization $ $ cat y.s .file "y.cpp" .section .text.unlikely,"ax",@progbits .LCOLDB0: .text .LHOTB0: .p2align 4,,15 .globl _Z3fooR1A .type _Z3fooR1A, @function _Z3fooR1A: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 pushq %rbx .cfi_def_cfa_offset 24 .cfi_offset 3, -24 movq %rdi, %rbp movl $1000, %ebx subq $8, %rsp .cfi_def_cfa_offset 32 .p2align 4,,10 .p2align 3 .L2: movq 0(%rbp), %rax movl $13, %esi movq %rbp, %rdi call *8(%rax) subl $1, %ebx jne .L2 addq $8, %rsp .cfi_def_cfa_offset 24 popq %rbx .cfi_def_cfa_offset 16 popq %rbp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE0: .size _Z3fooR1A, .-_Z3fooR1A .section .text.unlikely .LCOLDE0: .text .LHOTE0: .ident "GCC: (GNU) 5.3.1 20160406 (Red Hat 5.3.1-6)" .section .note.GNU-stack,"",@progbits $
Je serais plus préoccupé par le cache Miss et la cible de saut mal prédite que le temps nécessaire pour chercher en soi. Et ces problèmes dépendent beaucoup des schémas précis des accès antérieurs. La réponse standard sur la comparaison de la micro-performance était "Mesure" et c'est maintenant "mesurer et faire attention à ce que vous mesurez est vraiment caractéristique du déploiement". Vous aurez également une dépendance sur le modèle précis du processeur, ce que les autres notes sont en cours d'exécution, des effets SMT, ...