Desempenho de Object.GetType ()

votos
38

Temos lotes de chamadas de registro em nosso aplicativo. Nosso logger recebe um parâmetro System.Type para que ele possa mostrar qual componente criado a chamada. Às vezes, quando podemos ser incomodado, nós fazer algo como:

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

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

Como isso requer recebendo o objeto Type apenas uma vez. No entanto, não temos quaisquer métricas reais sobre este assunto. Alguém tem alguma idéia de quanto isso poupa sobre chamando this.GetType () cada vez que logar?

(Eu percebo que eu poderia fazer as métricas-me com nenhum grande problema, mas hey, o que é StackOverflow para?)

Publicado 09/12/2008 em 17:20
fonte usuário
Em outras línguas...                            


6 respostas

votos
70

Eu suspeito fortemente que GetType () terá muito menos tempo do que qualquer colheita real. Claro, há a possibilidade de que a sua chamada para Logger.log não vai fazer qualquer IO real ... Eu ainda suspeitar que a diferença será irrelevante embora.

EDIT: Código de Referência está no fundo. Resultados:

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

Que está chamando o método de 100 milhões de vezes - os ganhos de otimização de um par de segundos ou assim. Eu suspeito que o método de registro de verdade vai ter muito mais trabalho a fazer, e chamando que 100 milhões de vezes vai levar muito mais tempo do que 4 segundos no total, mesmo se ele não escreve nada. (Eu posso estar errado, é claro - você tem que tentar isso mesmo.)

Em outras palavras, como normal, eu iria com o código mais legível, em vez de micro-otimização.

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;
    }
}
Respondeu 09/12/2008 em 17:45
fonte usuário

votos
15

A GetType()função está marcada com o atributo especial [MethodImpl(MethodImplOptions.InternalCall)]. Isto significa que o seu corpo método não contém IL, mas em vez disso é um gancho para dentro da parte interna do .NET CLR. Neste caso, ele olha para a estrutura binária de metadados do objeto e constrói um System.Typeobjeto em torno dele.

EDIT: Eu acho que eu estava errado sobre alguma coisa ...

Eu disse que: "porque GetType()requer um novo objeto para ser construído", mas parece que isso não é correto. De alguma forma, o CLR armazena em cache o Typee sempre retorna o mesmo objeto para que ele não precisa construir um novo objeto Type.

Eu estou baseado no seguinte teste:

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

Então, eu não esperava muito ganho na sua implementação.

Respondeu 09/12/2008 em 17:46
fonte usuário

votos
7

Eu duvido que você está indo para obter uma resposta satisfatória do SO sobre este assunto. A razão é que o desempenho, especialmente cenários deste tipo, são altamente aplicação específica.

Alguém pode enviar de volta com um exemplo cronômetro rápida do que seria mais rápido em termos de milisegundos matérias. Mas, francamente, isso não significa nada para a sua aplicação. Por quê? Ele depende muito do padrão de uso em torno desse cenário particular. Por exemplo ...

  1. Quantos tipos que você tem?
  2. Quão grande és tu métodos?
  3. Você fazer isso para cada método ou apenas grandes?

Estas são apenas algumas das perguntas que irão alterar significativamente a relevância de um referencial horário reta.

Respondeu 09/12/2008 em 17:46
fonte usuário

votos
2

A diferença é provavelmente insignificante, tanto quanto o desempenho do aplicativo está em causa. Mas a primeira abordagem onde o cache do tipo deve ser mais rápido. Vamos teste.

Este código irá mostrar a diferença:

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) {
        }
    }
}

Na minha máquina, isso deu resultados de aprox. 1500 milissegundos para a primeira abordagem e aprox. 2200 milissegundos para o segundo.

(Código e horários corrigido - doh!)

Respondeu 09/12/2008 em 17:47
fonte usuário

votos
0

Já considerou usando nameof operador?

Respondeu 11/09/2017 em 06:42
fonte usuário

votos
0

usando campo é a melhor maneira e evitar bloqueio dicionário interno causando por typeof () e GetType () para manter de referência único.

Respondeu 30/12/2014 em 14:45
fonte usuário

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