Tengo un programa que requiere un rendimiento rápido. Dentro de uno de sus bucles internos, necesito probar el tipo de un objeto para ver si hereda de una determinada interfaz.
Una forma de hacerlo sería con la función de verificación de tipo incorporada de CLR. El método más elegante probablemente sea la palabra clave 'es':
if (obj is ISpecialType)
Otro enfoque sería darle a la clase base mi propia función virtual GetType () que devuelve un valor de enumeración predefinido (en mi caso, en realidad, solo necesito un bool). Ese método sería rápido, pero menos elegante.
Escuché que hay una instrucción IL específicamente para la palabra clave 'es', pero eso no significa que se ejecute rápidamente cuando se traduzca al ensamblado nativo. ¿Alguien puede compartir alguna idea sobre el rendimiento de 'is' versus el otro método?
ACTUALIZACIÓN: ¡ Gracias por todas las respuestas informadas! Parece que hay un par de puntos útiles repartidos entre las respuestas: el punto de Andrew acerca de 'es' realizar un yeso automáticamente es esencial, pero los datos de rendimiento recopilados por Binary Worrier e Ian también son extremadamente útiles. Sería fantástico si se editara una de las respuestas para incluir toda esta información.
fuente
Respuestas:
Usar
is
puede dañar el rendimiento si, una vez que verifica el tipo, lanza a ese tipo.is
en realidad, envía el objeto al tipo que está verificando, por lo que cualquier conversión posterior es redundante.Si vas a lanzar de todos modos, aquí tienes un mejor enfoque:
fuente
as
básicamente realiza la misma operación queis
(es decir, la verificación de tipo). La única diferencia es que luego regresa ennull
lugar defalse
.Estoy con Ian , probablemente no quieras hacer esto.
Sin embargo, para que lo sepas, hay muy poca diferencia entre los dos, más de 10,000,000 iteraciones
Personalmente, no solucionaría este problema de esta manera, pero si me viera obligado a elegir un método sería la verificación de IS incorporada, la diferencia de rendimiento no vale la pena considerar la sobrecarga de codificación.
Mis clases base y derivadas
JubJub: Como se solicitó más información sobre las pruebas.
Ejecuté ambas pruebas desde una aplicación de consola (una compilación de depuración), cada prueba se parece a la siguiente
Al ejecutar la versión, obtengo una diferencia de 60 a 70 ms, como Ian.
Actualización adicional - 25 de octubre de 2012
Después de un par de años de distancia, noté algo sobre esto, el compilador puede optar por omitir
bool b = a is MyClassB
en la versión porque b no se usa en ninguna parte.Este código. . .
. . . muestra consistentemente que el
is
cheque llega a aproximadamente 57 milisegundos, y la comparación de enumeración llega a 29 milisegundos.Nota : prefiero el
is
cheque, la diferencia es demasiado pequeña como para preocuparsefuente
is
operador te esté causando, y que todo lo que se ha oído sobre el diseño y la codificación en torno alis
operador costará una fortuna. calidad del código y, en última instancia, también será contraproducente en cuanto a rendimiento. En este caso, mantengo mi declaración. El operador 'is' nunca será el problema con el rendimiento en tiempo de ejecución.Ok, estaba charlando sobre esto con alguien y decidí probarlo más. Por lo que puedo decir, el rendimiento de
as
yis
es muy bueno, en comparación con probar su propio miembro o función para almacenar información de tipo.Usé
Stopwatch
, que acabo de aprender que puede no ser el enfoque más confiable, así que también lo intentéUtcNow
. Más tarde, también probé el enfoque del tiempo del procesador, que parece similar aUtcNow
incluir tiempos de creación impredecibles. También intenté hacer que la clase base no fuera abstracta sin virtuales, pero no pareció tener un efecto significativo.Ejecuté esto en un Quad Q6600 con 16 GB de RAM. Incluso con iteraciones de 50 mil, los números todavía rebotan alrededor de +/- 50 milisegundos o más, por lo que no leería demasiado en las diferencias menores.
Fue interesante ver que x64 se creaba más rápido pero se ejecutaba como / es más lento que x86
Modo de lanzamiento x64:
Cronómetro:
Como: 561ms
Es: 597ms
Propiedad base: 539ms
Campo base: 555ms
Campo RO base: 552ms Prueba
virtual GetEnumType (): 556ms Prueba
virtual IsB (): 588ms
Tiempo de creación: 10416ms
UtcNow:
As: 499ms
Is: 532ms
Propiedad base: 479ms
Campo base: 502ms
Campo RO base: 491ms
Virtual GetEnumType (): 502ms
Virtual bool IsB (): 522ms
Tiempo de creación: 285ms (Este número parece poco confiable con UtcNow. También obtengo 109ms y 806ms.)
Modo de lanzamiento x86:
Cronómetro:
Como: 391ms
Es: 423ms
Propiedad base: 369ms
Campo base: 321ms
Campo RO base: 339ms Prueba
virtual GetEnumType (): 361ms Prueba
virtual IsB (): 365ms
Tiempo de creación: 14106ms
UtcNow:
As: 348ms
Is: 375ms
Propiedad base: 329ms
Campo base: 286ms
Campo RO base: 309ms
Virtual GetEnumType (): 321ms
Virtual bool IsB (): 332ms
Tiempo de creación: 544ms (Este número parece poco confiable con UtcNow.)
Aquí está la mayor parte del código:
fuente
Andrew tiene razón. De hecho, con el análisis de código, Visual Studio informa de esto como un reparto innecesario.
Una idea (sin saber lo que estás haciendo es un poco disparatada en la oscuridad), pero siempre me han aconsejado que evite marcar así y que en su lugar tenga otra clase. Entonces, en lugar de hacer algunas verificaciones y tener diferentes acciones según el tipo, haga que la clase sepa cómo procesarse a sí misma ...
por ejemplo, Obj puede ser ISpecialType o IType;
ambos tienen un método DoStuff () definido. Para IType, solo puede regresar o hacer cosas personalizadas, mientras que ISpecialType puede hacer otras cosas.
Esto luego elimina por completo cualquier conversión, hace que el código sea más limpio y más fácil de mantener, y la clase sabe cómo hacer sus propias tareas.
fuente
Hice una comparación de rendimiento en dos posibilidades de comparación de tipos
El resultado es: ¡Usar "es" es aproximadamente 10 veces más rápido!
Salida:
Hora de comparación de tipos: 00: 00: 00.456
Hora de comparación Is: 00: 00: 00.042
Mi código:
fuente
El punto que Andrew Hare hizo sobre la pérdida de rendimiento cuando realizaste la
is
verificación y luego el lanzamiento fue válido, pero en C # 7.0 podemos hacer es verificar la coincidencia del patrón de brujas para evitar un lanzamiento adicional más adelante:Además, si necesita verificar entre varios tipos, las construcciones de coincidencia de patrones de C # 7.0 ahora le permiten hacer
switch
en tipos:Puede leer más sobre la coincidencia de patrones en C # en la documentación aquí .
fuente
OneOf<T...>
pero tienen deficiencias importantes) .En caso de que alguien se lo pregunte, hice pruebas en el motor Unity 2017.1, con scripting runtime versión .NET4.6 (Experimantal) en un portátil con CPU i5-4200U. Resultados:
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
Artículo completo: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
fuente
Siempre me han aconsejado que evite marcar de esta manera y que en su lugar tenga otra clase. Entonces, en lugar de hacer algunas comprobaciones y tener diferentes acciones según el tipo, haga que la clase sepa cómo procesarse a sí misma ...
por ejemplo, Obj puede ser ISpecialType o IType;
ambos tienen un método DoStuff () definido. Para IType, solo puede regresar o hacer cosas personalizadas, mientras que ISpecialType puede hacer otras cosas.
Esto luego elimina por completo cualquier conversión, hace que el código sea más limpio y más fácil de mantener, y la clase sabe cómo hacer sus propias tareas.
fuente