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


7 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

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

votos
0

Já considerou usando nameof operador?

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

votos
-1

Eu obter resultados muito diferentes.
Para isso eu criei um novo aplicativo de console em outro projeto, e usou uma classe com herança.

Criei um loop vazio para retirar a partir dos resultados, para uma comparação limpo.
Criei um const e um estático para os ciclos (comutação manualmente qual usar).
Algo muito interessante aconteceu.

Quando se utiliza o const, o loop vazio tornar-se lenta, mas o teste var tamponada torna-se ligeiramente mais depressa.
Uma mudança que deve afetar testes nenhum ou todos, afetam apenas 2.

Ciclos para cada teste: 100000000

Usando o ciclo de estática:

Object.GetType: 1316
TypeOf (Classe): 1589
Tipo var: 987
Loop vazio: 799

visão geral limpa:
Object.GetType: 517
TypeOf (Classe): 790
Tipo var: 188

Usando ciclo const:

Object.GetType: 1316
TypeOf (Classe): 1583
Tipo var: 853
Laço vazio: 1061

visão geral limpa:
Object.GetType: 255
TypeOf (Classe): 522
Tipo var: -208

Corri estas várias vezes, e com algumas pequenas alterações, e com 10 vezes mais ciclos, para reduzir o risco de processos de fundo que afetam os resultados. Mesmos resultados quase como estes dois acima.

Parece que Object.GetType()é 1,5-2 vezes tão rápido quanto typeof(class).
O var tamponada parecem ser 1,5-2 vezes tão rápido quanto Object.GetType().

Eu o aplicativo certo, este não é apenas micro-otimização.
Se você sacrificar pequenas coisas aqui e ali, eles serão facilmente desacelerar mais do que o grande coisa que você fez 30% mais rápido.

Novamente como JaredPar respondeu, este tipo de testes, não é confiável para contar sobre a sua aplicação específica, como temos provado aqui.
Todos os nossos testes que dão resultados bastante diferentes, e as coisas aparentemente não relacionados ao código na mão, pode afetar o desempenho.

O teste:

.NetCore 2.1
namespace ConsoleApp1
{
    class Program
    {
        public const int Cycles = 100000000;
        public static int Cycles2 = 100000000;
        public static QSData TestObject = new QSData();
        public static Type TestObjectType;

        static void Main(string[] args)
        {
            TestObjectType = TestObject.GetType();
            Console.WriteLine("Repeated cycles for each test : " + Cycles.ToString());

            var test1 = TestGetType();
            Console.WriteLine("Object.GetType : " + test1.ToString());
            var test2 = TestTypeOf();
            Console.WriteLine("TypeOf(Class)  : " + test2.ToString());
            var test3 = TestVar();
            Console.WriteLine("Type var       : " + test3.ToString());
            var test4 = TestEmptyLoop();
            Console.WriteLine("Empty Loop     : " + test4.ToString());

            Console.WriteLine("\r\nClean overview:");
            Console.WriteLine("Object.GetType : " + (test1 - test4).ToString());
            Console.WriteLine("TypeOf(Class)  : " + (test2 - test4).ToString());
            Console.WriteLine("Type var       : " + (test3 - test4).ToString());

            Console.WriteLine("\n\rPush a button to exit");
            String input = Console.ReadLine();
        }

        static long TestGetType()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < Cycles; i++)
            {
                Type aType = TestObject.GetType();
            }
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        static long TestTypeOf()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < Cycles; i++)
            {
                Type aType = typeof(QSData);
            }
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        static long TestVar()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < Cycles; i++)
            {
                Type aType = TestObjectType;
            }
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }

        static long TestEmptyLoop()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            for (int i = 0; i < Cycles; i++)
            {
                Type aType;
            }
            stopwatch.Stop();
            return stopwatch.ElapsedMilliseconds;
        }
    }
}
Respondeu 23/04/2019 em 03:32
fonte usuário

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