J'ai un programme qui nécessite des performances rapides. Dans l'une de ses boucles internes, je dois tester le type d'un objet pour voir s'il hérite d'une certaine interface.
Une façon de procéder serait d'utiliser la fonctionnalité de vérification de type intégrée du CLR. La méthode la plus élégante est probablement le mot-clé 'is':
if (obj is ISpecialType)
Une autre approche serait de donner à la classe de base ma propre fonction virtuelle GetType () qui retourne une valeur d'énumération prédéfinie (dans mon cas, en fait, je n'ai besoin que d'un booléen). Cette méthode serait rapide, mais moins élégante.
J'ai entendu dire qu'il existe une instruction IL spécifiquement pour le mot clé «est», mais cela ne signifie pas qu'il s'exécute rapidement lorsqu'il est traduit en assemblage natif. Quelqu'un peut-il partager un aperçu de la performance de «est» par rapport à l'autre méthode?
MISE À JOUR: Merci pour toutes les réponses éclairées! Il semble que quelques points utiles soient répartis entre les réponses: le point d'Andrew sur le fait de «exécuter» automatiquement un casting est essentiel, mais les données de performance recueillies par Binary Worrier et Ian sont également extrêmement utiles. Ce serait formidable si l'une des réponses était modifiée pour inclure toutes ces informations.
la source
Réponses:
L'utilisation
is
peut nuire aux performances si, une fois que vous avez vérifié le type, vous effectuez un cast vers ce type.is
lance réellement l'objet dans le type que vous vérifiez, de sorte que toute conversion ultérieure est redondante.Si vous envisagez de lancer de toute façon, voici une meilleure approche:
la source
as
effectue essentiellement la même opération queis
(à savoir, la vérification de type). La seule différence est qu'il revient ensuitenull
au lieu defalse
.Je suis avec Ian , tu ne veux probablement pas faire ça.
Cependant, pour que vous le sachiez, il y a très peu de différence entre les deux, plus de 10000000 itérations
Personnellement, je ne résoudrais pas ce problème de cette façon, mais si j'étais obligé de choisir une méthode, ce serait le contrôle intégré du SI, la différence de performance ne vaut pas la peine de prendre en compte la surcharge de codage.
Mes classes de base et dérivées
JubJub: Comme demandé plus d'informations sur les tests.
J'ai exécuté les deux tests à partir d'une application console (une version de débogage) chaque test ressemble à ce qui suit
En cours de sortie, j'obtiens une différence de 60 à 70 ms, comme Ian.
Mise à jour supplémentaire - 25 octobre 2012
Après quelques années, j'ai remarqué quelque chose à ce sujet, le compilateur peut choisir d'omettre
bool b = a is MyClassB
dans la version car b n'est utilisé nulle part.Ce code. . .
. . . montre systématiquement le
is
contrôle arrivant à environ 57 millisecondes et la comparaison d'énumération à 29 millisecondes.NB je préfère quand même le
is
chèque, la différence est trop petite pour m'en soucierla source
is
opérateur vous cause, et que le surentendu de la conception et du codage autour de l'is
opérateur coûtera une fortune en la qualité du code et sera finalement autodestructrice en termes de performances. Dans ce cas, je maintiens ma déclaration. L'opérateur «est» ne sera jamais le problème avec vos performances d'exécution.Ok, donc j'ai discuté de cela avec quelqu'un et j'ai décidé de tester cela davantage. Autant que je sache, les performances
as
etis
sont toutes deux très bonnes, comparées au test de votre propre membre ou fonction pour stocker des informations de type.J'ai utilisé
Stopwatch
, ce que je viens d'apprendre n'est peut-être pas l'approche la plus fiable, alors j'ai également essayéUtcNow
. Plus tard, j'ai également essayé l'approche du temps du processeur qui semble similaire à l'UtcNow
inclusion de temps de création imprévisibles. J'ai également essayé de rendre la classe de base non abstraite sans virtuels, mais cela ne semblait pas avoir d'effet significatif.J'ai couru cela sur un Quad Q6600 avec 16 Go de RAM. Même avec des itérations de 50 mil, les chiffres rebondissent toujours autour de +/- 50 millisecondes, donc je ne lirais pas trop les différences mineures.
Il était intéressant de voir que x64 créé plus rapidement mais exécuté comme / est plus lent que x86
Mode de sortie x64:
Chronomètre:
As: 561 ms
Est: 597 ms
Propriété de base: 539 ms
Champ de base: 555 ms
Champ RO de base: 552
ms Test virtuel GetEnumType (): 556
ms Test Virtual IsB (): 588 ms
Temps de création: 10416 ms
UtcNow:
As: 499ms
Est: 532ms
Propriété de base: 479ms
Champ de base: 502ms
Champ RO de base: 491ms
Virtual GetEnumType (): 502ms
Virtual bool IsB (): 522ms
Temps de création: 285ms (Ce nombre semble peu fiable avec UtcNow. J'obtiens également 109ms et 806ms.)
x86 Release Mode:
Chronomètre:
As: 391ms
Est: 423ms
Propriété de base: 369ms
Champ de base: 321ms
Champ RO de base: 339ms Test
virtuel GetEnumType (): 361ms Test
Virtual IsB (): 365ms
Temps de création: 14106ms
UtcNow:
As: 348ms
Est: 375ms
Propriété de base: 329ms
Champ de base: 286ms
Champ RO de base: 309ms
Virtual GetEnumType (): 321ms
Virtual bool IsB (): 332ms
Temps de création: 544ms (Ce nombre semble peu fiable avec UtcNow.)
Voici l'essentiel du code:
la source
Andrew a raison. En fait, avec l'analyse du code, cela est signalé par Visual Studio comme une distribution inutile.
Une idée (sans savoir ce que vous faites est un peu une photo dans le noir), mais on m'a toujours conseillé d'éviter de vérifier comme ça, et d'avoir à la place une autre classe. Alors plutôt que de faire quelques vérifications et d'avoir des actions différentes selon le type, faites savoir à la classe comment se traiter ...
par exemple, Obj peut être ISpecialType ou IType;
les deux ont une méthode DoStuff () définie. Pour IType, il peut simplement renvoyer ou effectuer des tâches personnalisées, tandis que ISpecialType peut faire d'autres choses.
Cela supprime alors complètement tout casting, rend le code plus propre et plus facile à maintenir, et la classe sait comment faire ses propres tâches.
la source
J'ai fait une comparaison des performances sur deux possibilités de comparaison de types
Le résultat est: l'utilisation de "is" est environ 10 fois plus rapide !!!
Production:
Heure de la comparaison de types: 00: 00: 00.456
Heure de la comparaison Is: 00: 00: 00.042
Mon code:
la source
Andrew Hare a fait remarquer que les performances
is
étaient perdues lorsque vous effectuez une vérification, puis que la conversion était valide, mais en C # 7.0, nous pouvons faire est de vérifier la correspondance du modèle de sorcière pour éviter une distribution supplémentaire plus tard:En outre, si vous avez besoin de vérifier entre plusieurs types, les constructions de correspondance de modèles C # 7.0 vous permettent désormais de faire
switch
sur les types:Vous pouvez en savoir plus sur la correspondance de modèles en C # dans la documentation ici .
la source
OneOf<T...>
mais elles ont des inconvénients majeurs) .Au cas où quelqu'un se demanderait, j'ai effectué des tests dans le moteur Unity 2017.1, avec la version d'exécution de script .NET4.6 (Experimantal) sur un ordinateur portable avec un processeur i5-4200U. Résultats:
Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35
Article complet: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
la source
On m'a toujours conseillé d'éviter de vérifier comme ça et d'avoir à la place une autre classe. Alors plutôt que de faire quelques vérifications et d'avoir des actions différentes selon le type, faites savoir à la classe comment se traiter ...
par exemple, Obj peut être ISpecialType ou IType;
les deux ont une méthode DoStuff () définie. Pour IType, il peut simplement renvoyer ou effectuer des tâches personnalisées, tandis que ISpecialType peut faire d'autres choses.
Cela supprime alors complètement tout casting, rend le code plus propre et plus facile à maintenir, et la classe sait comment faire ses propres tâches.
la source