Question

Avant de lire la question:
Cette question ne concerne pas son utilité dynamic_cast. C'est juste sa performance.

J'ai récemment développé un design où dynamic_cast est beaucoup utilisé.
Lorsque vous en discutez avec des collègues, presque tout le monde dit que dynamic_cast ne devrait pas être utilisé en raison de ses mauvaises performances (ce sont des collègues qui ont des antécédents différents et, dans certains cas, ne se connaissent pas. Je travaille dans une énorme entreprise)

J'ai décidé de tester les performances de cette méthode au lieu de simplement les croire.

Le code suivant a été utilisé:

ptime firstValue( microsec_clock::local_time() );

ChildObject* castedObject = dynamic_cast<ChildObject*>(parentObject);

ptime secondValue( microsec_clock::local_time() );
time_duration diff = secondValue - firstValue;
std::cout << "Cast1 lasts:\t" << diff.fractional_seconds() << " microsec" << std::endl;

Le code ci-dessus utilise des méthodes de boost::date_time sur Linux pour obtenir des valeurs utilisables.
J'ai fait 3 dynamic_cast Dans une exécution, le code pour les mesurer est le même.

Les résultats de 1 exécution étaient les suivants:
Cast1 dure: 74 microsec
Cast2 dure: 2 microsec
Cast3 dure: 1 microsec

Le premier casting a toujours pris 74-111 Microsec, les moulages suivants dans la même exécution ont pris 1 à 3 microsec.

Alors enfin mes questions:
Est dynamic_cast vraiment jouer mal?
Selon les TestResults, ce n'est pas le cas. Mon code de test est-il correct?
Pourquoi tant de développeurs pensent-ils que c'est lent si ce n'est pas le cas?

Était-ce utile?

La solution

Premièrement, vous devez mesurer les performances sur bien plus que quelques itérations, car vos résultats seront dominés par la résolution de la minuterie. Essayez par exemple 1 million +, afin de construire une image représentative. De plus, ce résultat est dénué de sens à moins que vous ne le comparez à quelque chose, c'est-à-dire en faisant l'équivalent mais sans la coulée dynamique.

Deuxièmement, vous devez vous assurer que le compilateur ne vous donne pas de faux résultats en optimisant plusieurs moulages dynamiques sur le même pointeur (alors utilisez une boucle, mais utilisez un pointeur d'entrée différent à chaque fois).

La coulée dynamique sera plus lente, car elle doit accéder à la table RTTI (Informations sur le type d'exécution) pour l'objet et vérifier que le casting est valide. Ensuite, afin de l'utiliser correctement, vous devrez ajouter du code de gestion des erreurs qui vérifie si le pointeur renvoyé est NULL. Tout cela prend des cycles.

Je sais que vous ne vouliez pas en parler, mais "un design où Dynamic_cast est beaucoup utilisé" est probablement un indicateur que vous faites quelque chose de mal ...

Autres conseils

La performance n'a pas de sens sans comparer des fonctionnalités équivalentes. La plupart des gens disent que Dynamic_cast est lent sans comparer à un comportement équivalent. Appelez-les à ce sujet. En d'autres termes:

Si «fonctionne» n'est pas une exigence, je peux écrire du code qui échoue plus rapidement que le vôtre.

Il existe différentes façons d'implémenter Dynamic_Cast, et certaines sont plus rapides que d'autres. Stroustrup a publié un article sur l'utilisation Prime pour améliorer Dynamic_cast, par exemple. Malheureusement, il est inhabituel de contrôler la façon dont votre compilateur implémente le casting, mais si les performances vous importent vraiment, vous avez le contrôle du compilateur que vous utilisez.

Cependant, n'utilise pas dynamic_cast toujours Soyez plus rapide que de l'utiliser - mais si vous n'avez pas vraiment besoin de Dynamic_cast, alors ne l'utilisez pas! Si vous avez besoin d'une recherche dynamique, il y aura des frais généraux, et vous pouvez alors comparer diverses stratégies.

Voici quelques repères:
http://tinodidriksen.com/2010/04/14/cpp-dynamic-cast-performance/
http://www.nerdblog.com/2006/12/how-slow-is-dynamiccast.html

Selon eux, Dynamic_cast est 5-30 fois plus lent que Reinterpret_cast, et la meilleure alternative fonctionne presque la même chose que Reinterpret_cast.

Je citerai la conclusion du premier article:

  • Dynamic_cast est lent pour tout sauf le casting au type de base; Ce casting particulier est optimisé
  • Le niveau d'héritage a un grand impact sur Dynamic_cast
  • la variable membre + Reinterpret_cast est le moyen le plus rapide de
    déterminer le type; Cependant, cela a des frais généraux de maintenance beaucoup plus élevés
    Lors du codage

Les nombres absolus sont de l'ordre de 100 ns pour une seule distribution. Des valeurs comme 74 ms ne semblent pas proches de la réalité.

Désolé de le dire, mais votre test est pratiquement inutile pour déterminer si le casting est lent ou non. La résolution de microsecondes est loin d'être assez bonne. Nous parlons d'une opération qui, même dans le pire des cas, ne devrait pas prendre plus que, disons, 100 tiques d'horloge ou moins de 50 nanosecondes sur un PC typique.

Il ne fait aucun doute que le casting dynamique sera plus lent qu'un casting statique ou une distribution réinterprétée, car, au niveau de l'assemblage, les deux derniers équivaudront à une affectation (très rapide, l'ordre de 1 horloge), et le casting dynamique nécessite Le code pour aller et inspecter l'objet pour déterminer son type réel.

Je ne peux pas dire à quel point c'est lent vraiment, cela varierait probablement d'un compilateur à un compilateur, j'aurais besoin de voir le code d'assemblage généré pour cette ligne de code. Mais, comme je l'ai dit, 50 nanosecondes par appel sont la limite supérieure de ce qui s'attend à être raisonnable.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top