Coup de performance des moulages de style C ++?

Je suis un débutant dans les transtypages de style C ++ et je crains que l’utilisation de transtypages de style C ++ ne vienne nuire aux performances de mon application car j’ai une échéance critique en temps réel dans ma routine interrupt-service.

J’ai entendu que certains moulages vont même jeter des exceptions!

Je voudrais utiliser les moulages de style C ++ car cela rendrait mon code plus “robuste”. Cependant, si vous rencontrez des problèmes de performances, je n’utiliserai probablement pas les transtypages de style C ++ et passerai plus de temps à tester le code qui utilise des transtypages de style C.


Quelqu’un a-t-il fait des tests / profils rigoureux pour comparer les performances des jets de style C ++ aux jets de style C?

Quels ont été vos résultats?

Quelles conclusions as-tu tirées?

Si la dissortingbution de style C ++ peut être conceptuellement remplacée par une dissortingbution de style C, il n’y aura pas de surcharge. Si ce n’est pas le cas, comme dans le cas de dynamic_cast , pour lequel il n’y a pas d’équivalent C, vous devez payer le coût d’une manière ou d’une autre.

Par exemple, le code suivant:

 int x; float f = 123.456; x = (int) f; x = static_cast(f); 

génère un code identique pour les deux versions avec VC ++ – le code est:

 00401041 fld dword ptr [ebp-8] 00401044 call __ftol (0040110c) 00401049 mov dword ptr [ebp-4],eax 

Le seul cast C ++ capable de lancer est dynamic_cast lors de la dynamic_cast vers une référence. Pour éviter cela, lancez un pointeur qui renverra 0 si la dissortingbution échoue.

Le seul avec un coût supplémentaire à l’exécution est dynamic_cast , qui dispose de capacités qui ne peuvent pas être reproduites directement avec une dissortingbution de style C. Donc, vous n’avez aucun problème.

Le moyen le plus simple de vous rassurer consiste à demander à votre compilateur de générer des données d’assemblage et d’examiner le code qu’il génère. Par exemple, dans tout compilateur implémenté de manière sereine, reinterpret_cast disparaîtra complètement, car cela signifie simplement “aller de l’avant aveuglément et prétendre que les données sont de ce type”.

Pourquoi y aurait-il un coup de performance? Ils exécutent exactement les mêmes fonctionnalités que C cast. La seule différence est qu’ils attrapent plus d’erreurs à la compilation et qu’ils sont plus faciles à rechercher dans votre code source.

static_cast(3) est exactement équivalent à (float)3 et générera exactement le même code.

Etant donné un float f = 42.0f reinterpret_cast(&f) est exactement équivalent à (int*)&f , et générera exactement le même code.

Etc. La seule dissortingbution qui diffère est dynamic_cast , qui, oui, peut lancer une exception. Mais c’est parce qu’il fait des choses que les acteurs de style C ne peuvent pas faire. Donc, n’utilisez pas dynamic_cast sauf si vous avez besoin de ses fonctionnalités.

Il est généralement prudent de supposer que les auteurs du compilateur sont intelligents. Étant donné que deux expressions différentes ont la même sémantique selon la norme, on peut généralement supposer qu’elles seront implémentées de manière identique dans le compilateur.

Oops : Le deuxième exemple devrait être reinterpret_cast, pas dynamic_cast, bien sûr. Corrigé maintenant.

Ok, juste pour être absolument clair, voici ce que dit le standard C ++:

§5.4.5:

Les conversions effectuées par

  • un const_cast (5.2.11)
  • un static_cast (5.2.9)
  • un static_cast suivi d’un const_cast
  • un reinterpret_cast (5.2.10), ou
  • un const_cast suivi d’un const_cast .

peut être effectuée en utilisant la notation de conversion de type explicite. Les mêmes ressortingctions et comportements sémantiques s’appliquent. Si une conversion peut être interprétée de plusieurs façons parmi celles énumérées ci-dessus, l’interprétation qui apparaît en premier dans la liste est utilisée, même si une dissortingbution résultant de cette interprétation est mal formée.

Donc, en tout cas, puisque la dissortingbution C-style est implémentée en termes de castings C ++, les casts de style C devraient être plus lents . (bien sûr, ce n’est pas le cas, car le compilateur génère le même code dans tous les cas, mais il est plus plausible que les versions de style C ++ soient plus lentes.)

Il existe quatre versions de style C ++:

  • const_cast
  • static_cast
  • reinterpret_cast
  • dynamic_cast

Comme déjà mentionné, les trois premiers sont des opérations de compilation. Il n’y a pas de pénalité d’exécution pour les utiliser. Ce sont des messages au compilateur indiquant que les données déclarées d’une manière doivent être accessibles d’une manière différente. “J’ai dit que c’était un int* , mais permettez-moi d’y accéder comme s’il s’agissait d’un caractère char* pointant vers la sizeof(int) char s” ou “J’ai dit que ces données étaient en lecture seule et maintenant fonction qui ne le modifiera pas, mais ne prend pas le paramètre comme référence const. ”

Mis à part la corruption de données par conversion au mauvais type et le dépassement des données (toujours possible avec les casts de style C), le problème d’exécution le plus courant avec ces casts est que les données déclarées const peuvent ne pas être converties en non-const. Lancer quelque chose déclaré const à non-const et le modifier est indéfini. Indéfini signifie que vous n’êtes même pas assuré d’avoir un crash .

dynamic_cast est une structure d’exécution qui doit avoir un coût d’exécution.

La valeur de ces moulages est qu’ils indiquent spécifiquement ce que vous essayez de lancer / viser, et que vous pouvez les rechercher avec des outils sans cervelle. Je recommanderais de les utiliser en utilisant des moulages de style C.

Lors de l’utilisation de dynamic_cast plusieurs vérifications sont effectuées lors de l’exécution pour vous empêcher de faire quelque chose de stupide (plus sur la liste de diffusion GCC ), le coût d’un dynamic_cast dépend du nombre de classes affectées, des classes affectées, etc.
Si vous êtes vraiment sûr que la dissortingbution est sûre, vous pouvez toujours utiliser reinterpret_cast .

Bien que je sois d’accord avec l’énoncé ” dynamic_cast est le seul avec un coût supplémentaire à l’exécution”, gardez à l’esprit qu’il peut y avoir des différences spécifiques au compilateur.

J’ai vu quelques bogues enregistrés contre mon compilateur actuel, où la génération ou l’optimisation du code était légèrement différente selon que vous static_cast cast static_cast style C static_cast style C ++.

Donc, si vous êtes inquiet, vérifiez le déassembly sur les points chauds. Sinon, évitez simplement les dissortingbutions dynamics lorsque vous n’en avez pas besoin. (Si vous désactivez RTTI, vous ne pouvez pas utiliser dynamic_cast toute façon.)

La vérité canonique est l’assemblage, alors essayez les deux et voyez si vous obtenez une logique différente.

Si vous obtenez exactement le même assemblage, il n’y a pas de différence, il ne peut y en avoir. Le seul endroit où vous devez vraiment restr avec les anciens modèles C est les bibliothèques et les routines C, où il est inutile d’introduire une dépendance C ++ uniquement pour le type casting.

Une chose à prendre en compte est que les lancers se produisent partout dans un morceau de code de taille décente. Au cours de toute ma carrière, je n’ai jamais cherché “tous les modèles” dans un morceau de logique – vous avez tendance à chercher des modèles à un type spécifique comme “A”, et une recherche sur “(A)” est généralement aussi efficace que quelque chose comme “static_cast “. Utilisez les nouvelles versions pour des choses telles que la validation de type, par exemple, car elles ne vous faciliteront jamais la tâche.