Rendimiento de Object.GetType ()

votos
38

Tenemos muchas llamadas de registro en nuestra aplicación. Nuestro registrador toma un parámetro System.Type para que pueda mostrar qué componente creó la llamada. A veces, cuando nos molestan, hacemos algo como:

class Foo
{
  private static readonly Type myType = typeof(Foo);

  void SomeMethod()
  {
     Logger.Log(myType, SomeMethod started...);
  }
 }

Como esto requiere obtener el objeto Tipo solo una vez. Sin embargo, no tenemos ninguna métrica real sobre esto. Alguien tiene alguna idea de cuánto ahorra por llamar a this.GetType () cada vez que inicia sesión?

(Me doy cuenta de que podría hacer las métricas sin grandes problemas, pero, ¿para qué sirve StackOverflow?)

Publicado el 09/12/2008 a las 17:20
fuente por usuario
En otros idiomas...                            


6 respuestas

votos
70

Sospecho fuertemente que GetType () tomará mucho menos tiempo que cualquier registro real. Por supuesto, existe la posibilidad de que su llamada a Logger.Log no haga ningún IO real ... Aun así, sospecho que la diferencia será irrelevante.

EDITAR: El código de referencia está en la parte inferior. Resultados:

typeof(Test): 2756ms
TestType (field): 1175ms
test.GetType(): 3734ms

Eso es llamar al método 100 millones de veces, la optimización gana un par de segundos más o menos. Sospecho que el método de registro real tendrá mucho más trabajo que hacer, y llamar a eso 100 millones de veces tomará mucho más de 4 segundos en total, incluso si no escribe nada. (Podría estar equivocado, por supuesto, tendrías que intentarlo tú mismo).

En otras palabras, como es normal, elegiría el código más legible en lugar de la micro-optimización.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

class Test
{
    const int Iterations = 100000000;

    private static readonly Type TestType = typeof(Test);

    static void Main()
    {
        int total = 0;
        // Make sure it's JIT-compiled
        Log(typeof(Test)); 

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            total += Log(typeof(Test));
        }
        sw.Stop();
        Console.WriteLine("typeof(Test): {0}ms", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            total += Log(TestType);
        }
        sw.Stop();
        Console.WriteLine("TestType (field): {0}ms", sw.ElapsedMilliseconds);

        Test test = new Test();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            total += Log(test.GetType());
        }
        sw.Stop();
        Console.WriteLine("test.GetType(): {0}ms", sw.ElapsedMilliseconds);
    }

    // I suspect your real Log method won't be inlined,
    // so let's mimic that here
    [MethodImpl(MethodImplOptions.NoInlining)]
    static int Log(Type type)
    {
        return 1;
    }
}
Respondida el 09/12/2008 a las 17:45
fuente por usuario

votos
15

La GetType()función está marcada con el atributo especial [MethodImpl(MethodImplOptions.InternalCall)]. Esto significa que su cuerpo de método no contiene IL, sino que es un gancho en las partes internas de .NET CLR. En este caso, observa la estructura binaria de los metadatos del objeto y construye un System.Typeobjeto a su alrededor.

EDITAR: Creo que estaba equivocado sobre algo ...

Dije eso: "porque GetType()requiere un nuevo Objeto para ser construido", pero parece que esto no es correcto. De alguna manera, el CLR almacena en caché Typey siempre devuelve el mismo objeto, por lo que no necesita construir un nuevo objeto Type.

Estoy basado en la siguiente prueba:

Object o1 = new Object();
Type t1 = o1.GetType();
Type t2 = o1.GetType();
if (object.ReferenceEquals(t1,t2))
    Console.WriteLine("same reference");

Por lo tanto, no espero mucha ganancia en su implementación.

Respondida el 09/12/2008 a las 17:46
fuente por usuario

votos
7

Dudo que vayan a obtener una respuesta satisfactoria de SO sobre este tema. La razón es que el rendimiento, especialmente los escenarios de este tipo, son altamente específicos de la aplicación.

Alguien puede publicar con un rápido ejemplo de cronómetro que sería más rápido en términos de milisegundos sin procesar. Pero, francamente, eso no significa nada para su aplicación. ¿Por qué? Depende en gran medida del patrón de uso en ese escenario particular. Por ejemplo ...

  1. ¿Cuántos tipos tienes?
  2. ¿Qué tan grandes son tus métodos?
  3. ¿Haces esto para cada método, o solo para los grandes?

Estas son solo algunas de las preguntas que alterarán en gran medida la relevancia de un punto de referencia en tiempo real.

Respondida el 09/12/2008 a las 17:46
fuente por usuario

votos
2

La diferencia es probablemente insignificante en lo que respecta al rendimiento de la aplicación. Pero el primer enfoque donde almacena en caché el tipo debe ser más rápido. Vamos a probar.

Este código te mostrará la diferencia:

using System;

namespace ConsoleApplicationTest {
    class Program {
        static void Main(string[] args) {

            int loopCount = 100000000;

            System.Diagnostics.Stopwatch timer1 = new System.Diagnostics.Stopwatch();
            timer1.Start();
            Foo foo = new Foo();
            for (int i = 0; i < loopCount; i++) {
                bar.SomeMethod();
            }
            timer1.Stop();
            Console.WriteLine(timer1.ElapsedMilliseconds);

            System.Diagnostics.Stopwatch timer2 = new System.Diagnostics.Stopwatch();
            timer2.Start();
            Bar bar = new Bar();
            for (int i = 0; i < loopCount; i++) {
                foo.SomeMethod();
            }
            timer2.Stop();
            Console.WriteLine(timer2.ElapsedMilliseconds);

            Console.ReadLine();
        }
    }

    public class Bar {
        public void SomeMethod() {
            Logger.Log(this.GetType(), "SomeMethod started...");
        }
    }

    public class Foo {
        private static readonly Type myType = typeof(Foo); 
        public void SomeMethod() { 
            Logger.Log(myType, "SomeMethod started..."); 
        }
    }

    public class Logger {
        public static void Log(Type type, string text) {
        }
    }
}

En mi máquina, esto dio resultados de aprox. 1500 milisegundos para el primer acercamiento y aprox. 2200 milisegundos para el segundo.

(código y tiempos corregidos - doh!)

Respondida el 09/12/2008 a las 17:47
fuente por usuario

votos
0

Ha considerado el uso nombredel operador?

Respondida el 11/09/2017 a las 03:42
fuente por usuario

votos
0

utilizando el campo es la mejor manera y evitar bloqueo interno diccionario causando por typeof () y GetType () para mantener referencia único.

Respondida el 30/12/2014 a las 14:45
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more