significa remarks que operator generate example c# performance clr gettype

remarks - que significa+= en c#



C#''es'' el rendimiento del operador (8)

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 cierta interfaz.

Una forma de hacerlo sería con la funcionalidad incorporada de comprobación de tipos del CLR. El método más elegante que 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 enum predefinido (en mi caso, en realidad, solo necesito un bool). Ese método sería rápido, pero menos elegante.

He oído que hay una instrucción IL específica para la palabra clave ''es'', pero eso no significa que se ejecuta rápidamente cuando se traduce al ensamblado nativo. ¿Alguien puede compartir alguna información sobre el desempeño de "es" 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 sobre "es" realizar automáticamente un reparto es esencial, pero los datos de rendimiento recopilados por Binary Worrier e Ian también son extremadamente útiles. Sería genial si se editara una de las respuestas para incluir toda esta información.


Andrew está en lo cierto. De hecho, con el análisis del código, Visual Studio lo informa como un molde innecesario.

Una idea (sin saber lo que estás haciendo es un poco oscura), pero siempre me han aconsejado que evite verificaciones como esta y, en cambio, tenga otra clase. Entonces, en lugar de hacer algunos controles y tener diferentes acciones según el tipo, haz 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, puede simplemente devolver o hacer cosas personalizadas, mientras que ISpecialType puede hacer otras cosas.

Esto luego elimina completamente cualquier fundició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.


El punto que Andrew Hare hizo sobre el rendimiento perdido cuando se realiza is verificar y luego el reparto fue válido, pero en C # 7.0 lo que podemos hacer es verificar la coincidencia del patrón de bruja para evitar el reparto adicional más adelante:

if (obj is ISpecialType st) { //st is in scope here and can be used }

Además, si necesita verificar entre varios tipos, las construcciones de coincidencia de patrones C # 7.0 ahora le permiten switch los tipos:

public static double ComputeAreaModernSwitch(object shape) { switch (shape) { case Square s: return s.Side * s.Side; case Circle c: return c.Radius * c.Radius * Math.PI; case Rectangle r: return r.Height * r.Length; default: throw new ArgumentException( message: "shape is not a recognized shape", paramName: nameof(shape)); } }

Puede leer más sobre la coincidencia de patrones en C # en la documentación aquí .


El uso puede perjudicar el rendimiento si, una vez que comprueba el tipo, lo lanza a ese tipo. is realidad arroja el objeto al tipo que está verificando para que cualquier conversión posterior sea redundante.

Si vas a lanzar de todos modos, aquí hay un mejor enfoque:

ISpecialType t = obj as ISpecialType; if (t != null) { // use t here }



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 de iteraciones

  • La verificación enum entra a 700 milisegundos (aprox)
  • El control IS viene a 1000 milisegundos (aprox)

Personalmente, no solucionaría este problema de esta manera, pero si tuviera que elegir un método sería la verificación IS integrada, la diferencia de rendimiento no vale la pena considerar la sobrecarga de codificación.

Mi base y clases derivadas

class MyBaseClass { public enum ClassTypeEnum { A, B } public ClassTypeEnum ClassType { get; protected set; } } class MyClassA : MyBaseClass { public MyClassA() { ClassType = MyBaseClass.ClassTypeEnum.A; } } class MyClassB : MyBaseClass { public MyClassB() { ClassType = MyBaseClass.ClassTypeEnum.B; } }

JubJub: A medida que solicite más información sobre las pruebas.

Ejecuté ambas pruebas desde una aplicación de consola (una versión de depuración), cada prueba se parece a la siguiente

static void IsTest() { DateTime start = DateTime.Now; for (int i = 0; i < 10000000; i++) { MyBaseClass a; if (i % 2 == 0) a = new MyClassA(); else a = new MyClassB(); bool b = a is MyClassB; } DateTime end = DateTime.Now; Console.WriteLine("Is test {0} miliseconds", (end - start).TotalMilliseconds); }

Corriendo en lanzamiento, 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, noté algo sobre esto, el compilador puede elegir omitir bool b = a is MyClassB en versión porque b no se usa en ninguna parte.

Este código. . .

public static void IsTest() { long total = 0; var a = new MyClassA(); var b = new MyClassB(); var sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 10000000; i++) { MyBaseClass baseRef; if (i % 2 == 0) baseRef = a;//new MyClassA(); else baseRef = b;// new MyClassB(); //bool bo = baseRef is MyClassB; bool bo = baseRef.ClassType == MyBaseClass.ClassTypeEnum.B; if (bo) total += 1; } sw.Stop(); Console.WriteLine("Is test {0} miliseconds {1}", sw.ElapsedMilliseconds, total); }

. . . muestra consistentemente que la verificación entra en aproximadamente 57 milisegundos, y la comparación de enum entra en 29 milisegundos.

NB , aún prefiero el cheque, la diferencia es demasiado pequeña para preocuparme por


Hice una comparación de rendimiento en dos posibilidades de comparación de tipos

  1. myobject.GetType () == typeof (MyClass)
  2. myobject es MyClass

El resultado es: ¡Usar "es" es aproximadamente 10 veces más rápido!

Salida:

Tiempo para la comparación de tipos: 00: 00: 00.456

Tiempo para la comparación de Is: 00: 00: 00.042

Mi código:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace ConsoleApplication3 { class MyClass { double foo = 1.23; } class Program { static void Main(string[] args) { MyClass myobj = new MyClass(); int n = 10000000; Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { bool b = myobj.GetType() == typeof(MyClass); } sw.Stop(); Console.WriteLine("Time for Type-Comparison: " + GetElapsedString(sw)); sw = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { bool b = myobj is MyClass; } sw.Stop(); Console.WriteLine("Time for Is-Comparison: " + GetElapsedString(sw)); } public static string GetElapsedString(Stopwatch sw) { TimeSpan ts = sw.Elapsed; return String.Format("{0:00}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds); } } }


Ok, estaba hablando de esto con alguien y decidí probarlo más. Por lo que puedo decir, el rendimiento de as y is son muy buenos, en comparación con la prueba de su propio miembro o función para almacenar información de tipo.

Stopwatch , que acabo de aprender puede no ser el enfoque más confiable, así que también probé UtcNow . Más tarde, también probé el enfoque del tiempo del procesador que parece similar a UtcNow incluidos 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 16GB de RAM. Incluso con iteraciones de 50 mil, los números todavía rebotan alrededor de +/- 50 o menos milisegundos, así que no leería demasiado sobre las pequeñas diferencias.

Fue interesante ver que x64 creó más rápido pero se ejecutó como / es más lento que x86

Modo de lanzamiento x64:
Cronógrafo:
Como: 561ms
Es: 597ms
Propiedad base: 539ms
Campo base: 555ms
Campo base RO: 552 ms
Prueba virtual GetEnumType (): 556ms
Prueba Virtual IsB (): 588ms
Crear tiempo: 10416ms

UtcNow:
Como: 499ms
Es: 532ms
Propiedad base: 479ms
Campo base: 502ms
Campo base RO: 491 ms
GetEnumType virtual (): 502ms
Virtual bool IsB (): 522ms
Crear tiempo: 285 ms (Este número parece poco fiable con UtcNow. También obtengo 109 ms y 806 ms).

Modo de lanzamiento x86:
Cronógrafo:
Como: 391ms
Es: 423ms
Propiedad base: 369ms
Campo base: 321ms
Campo base RO: 339ms
Prueba virtual GetEnumType (): 361ms
Prueba Virtual IsB (): 365ms
Crear tiempo: 14106ms

UtcNow:
Como: 348ms
Es: 375ms
Propiedad base: 329ms
Campo base: 286ms
Campo base RO: 309ms
GetEnumType virtual (): 321ms
Virtual bool IsB (): 332ms
Crear tiempo: 544ms (Este número parece poco confiable con UtcNow).

Aquí está la mayor parte del código:

static readonly int iterations = 50000000; void IsTest() { Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1; MyBaseClass[] bases = new MyBaseClass[iterations]; bool[] results1 = new bool[iterations]; Stopwatch createTime = new Stopwatch(); createTime.Start(); DateTime createStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { if (i % 2 == 0) bases[i] = new MyClassA(); else bases[i] = new MyClassB(); } DateTime createStop = DateTime.UtcNow; createTime.Stop(); Stopwatch isTimer = new Stopwatch(); isTimer.Start(); DateTime isStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i] is MyClassB; } DateTime isStop = DateTime.UtcNow; isTimer.Stop(); CheckResults(ref results1); Stopwatch asTimer = new Stopwatch(); asTimer.Start(); DateTime asStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i] as MyClassB != null; } DateTime asStop = DateTime.UtcNow; asTimer.Stop(); CheckResults(ref results1); Stopwatch baseMemberTime = new Stopwatch(); baseMemberTime.Start(); DateTime baseStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i].ClassType == MyBaseClass.ClassTypeEnum.B; } DateTime baseStop = DateTime.UtcNow; baseMemberTime.Stop(); CheckResults(ref results1); Stopwatch baseFieldTime = new Stopwatch(); baseFieldTime.Start(); DateTime baseFieldStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B; } DateTime baseFieldStop = DateTime.UtcNow; baseFieldTime.Stop(); CheckResults(ref results1); Stopwatch baseROFieldTime = new Stopwatch(); baseROFieldTime.Start(); DateTime baseROFieldStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B; } DateTime baseROFieldStop = DateTime.UtcNow; baseROFieldTime.Stop(); CheckResults(ref results1); Stopwatch virtMethTime = new Stopwatch(); virtMethTime.Start(); DateTime virtStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i].GetClassType() == MyBaseClass.ClassTypeEnum.B; } DateTime virtStop = DateTime.UtcNow; virtMethTime.Stop(); CheckResults(ref results1); Stopwatch virtMethBoolTime = new Stopwatch(); virtMethBoolTime.Start(); DateTime virtBoolStart = DateTime.UtcNow; for (int i = 0; i < iterations; i++) { results1[i] = bases[i].IsB(); } DateTime virtBoolStop = DateTime.UtcNow; virtMethBoolTime.Stop(); CheckResults(ref results1); asdf.Text += "Stopwatch: " + Environment.NewLine + "As: " + asTimer.ElapsedMilliseconds + "ms" + Environment.NewLine +"Is: " + isTimer.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base property: " + baseMemberTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base field: " + baseFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base RO field: " + baseROFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType() test: " + virtMethTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual IsB() test: " + virtMethBoolTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Create Time : " + createTime.ElapsedMilliseconds + "ms" + Environment.NewLine + Environment.NewLine+"UtcNow: " + Environment.NewLine + "As: " + (asStop - asStart).Milliseconds + "ms" + Environment.NewLine + "Is: " + (isStop - isStart).Milliseconds + "ms" + Environment.NewLine + "Base property: " + (baseStop - baseStart).Milliseconds + "ms" + Environment.NewLine + "Base field: " + (baseFieldStop - baseFieldStart).Milliseconds + "ms" + Environment.NewLine + "Base RO field: " + (baseROFieldStop - baseROFieldStart).Milliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType(): " + (virtStop - virtStart).Milliseconds + "ms" + Environment.NewLine + "Virtual bool IsB(): " + (virtBoolStop - virtBoolStart).Milliseconds + "ms" + Environment.NewLine + "Create Time : " + (createStop-createStart).Milliseconds + "ms" + Environment.NewLine; } } abstract class MyBaseClass { public enum ClassTypeEnum { A, B } public ClassTypeEnum ClassType { get; protected set; } public ClassTypeEnum ClassTypeField; public readonly ClassTypeEnum ClassTypeReadonlyField; public abstract ClassTypeEnum GetClassType(); public abstract bool IsB(); protected MyBaseClass(ClassTypeEnum kind) { ClassTypeReadonlyField = kind; } } class MyClassA : MyBaseClass { public override bool IsB() { return false; } public override ClassTypeEnum GetClassType() { return ClassTypeEnum.A; } public MyClassA() : base(MyBaseClass.ClassTypeEnum.A) { ClassType = MyBaseClass.ClassTypeEnum.A; ClassTypeField = MyBaseClass.ClassTypeEnum.A; } } class MyClassB : MyBaseClass { public override bool IsB() { return true; } public override ClassTypeEnum GetClassType() { return ClassTypeEnum.B; } public MyClassB() : base(MyBaseClass.ClassTypeEnum.B) { ClassType = MyBaseClass.ClassTypeEnum.B; ClassTypeField = MyBaseClass.ClassTypeEnum.B; } }


Siempre me han aconsejado que evite verificaciones como esta y, en su lugar, tenga otra clase. Entonces, en lugar de hacer algunos controles y tener diferentes acciones según el tipo, haz 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, puede simplemente devolver o hacer cosas personalizadas, mientras que ISpecialType puede hacer otras cosas.

Esto luego elimina completamente cualquier fundició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.