Performanța Object.GetType ()

voturi
38

Avem o mulțime de apeluri de logare în aplicația noastră. logger nostru ia un parametru System.Type astfel încât să poată afișa componenta care a creat apelul. Uneori, când putem fi deranjat, facem ceva de genul:

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

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

Deoarece aceasta necesită obținerea tipului de obiect doar o singură dată. Cu toate acestea nu avem nici valori reale în acest sens. Oricine are nici o idee cât de mult acest lucru economisește peste asteptare this.GetType () de fiecare dată când înregistrăm?

(Îmi dau seama că pot face valorile mine cu nici o problemă mare, dar hei, ce e StackOverflow pentru?)

Întrebat 09/12/2008 la 17:20
sursa de către utilizator
În alte limbi...                            


6 răspunsuri

voturi
70

Bănuiesc cu tărie că gettype () va dura mult mai puțin timp decât orice logare reală. Desigur, există posibilitatea ca apelul la Logger.Log nu va face nici un IO real ... eu încă bănuiesc diferența va fi irelevantă, totuși.

EDIT: Codul de referință este în partea de jos. Rezultate:

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

Asta sună metoda de 100 de milioane de ori - câștigurile de optimizare câteva secunde sau cam asa ceva. Bănuiesc metoda de logare reală va avea mult mai mult de lucru și făcând apel ca 100 de milioane de ori va dura mult mai mult de 4 secunde în total, chiar dacă nu scrie nimic. (Am putea fi greșit, desigur - ai avea să încerc asta te.)

Cu alte cuvinte, în mod normal, aș merge cu codul mai ușor de citit, mai degrabă decât micro-optimalizare.

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;
    }
}
Publicat 09/12/2008 la 17:45
sursa de către utilizator

voturi
15

GetType()Funcția este marcat cu atributul special [MethodImpl(MethodImplOptions.InternalCall)]. Acest lucru înseamnă că organismul său metodă nu conține IL , dar în schimb este un cârlig în CLR interne ale .NET. În acest caz, se uită la structura binară a metadatelor obiectului și construiește un System.Typeobiect în jurul acestuia.

EDIT: Cred că m - am înșelat în legătură cu ceva ...

I - am spus că: „ pentru că GetType()necesită un nou obiect care urmează să fie construit“ , dar se pare că acest lucru nu este corect. Într -un fel, CLR cache Typeși returnează întotdeauna același obiect , astfel că nu are nevoie pentru a construi un nou obiect de tip.

Am bazat pe următorul test:

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

Deci, eu nu mă aștept mult câștig în implementarea.

Publicat 09/12/2008 la 17:46
sursa de către utilizator

voturi
7

Mă îndoiesc că aveți de gând pentru a obține un răspuns satisfăcător la SO pe acest subiect. Motivul fiind că performanța, în special scenarii de acest tip, sunt extrem de aplicații specifice.

Cineva poate posta din nou cu un exemplu cronometru rapidă, care ar fi mai rapid în ceea ce privește prime milisecunde. Dar sincer asta nu înseamnă nimic pentru aplicația dumneavoastră. De ce? Aceasta depinde în mare măsură de modelul de utilizare în jurul valorii de acest scenariu special. De exemplu ...

  1. Câte tipuri ai?
  2. Cât de mari sunt metode de tine?
  3. Ai face acest lucru pentru fiecare metodă, sau numai pe cele mari?

Acestea sunt doar câteva din întrebările care vor modifica în mare măsură relevanța unui criteriu de referință timp drept.

Publicat 09/12/2008 la 17:46
sursa de către utilizator

voturi
2

Diferența este, probabil, neglijabilă în ceea ce privește performanța de aplicare este în cauză. Dar prima abordare în care cache tipul ar trebui să fie mai rapid. Să mergem și de testare.

Acest cod va arăta 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) {
        }
    }
}

Pe masina mea, acest lucru a dat rezultate de aprox. 1500 milisecunde pentru prima abordare și cca. 2200 milisecunde pentru a doua.

(Cod și timing corectat - doh!)

Publicat 09/12/2008 la 17:47
sursa de către utilizator

voturi
0

V - ați gândit să utilizați nameof operatorul?

Publicat 11/09/2017 la 06:42
sursa de către utilizator

voturi
0

folosind câmp este cel mai bun mod de a evita blocarea și dicționar intern determinând prin typeof () și gettype () pentru a menține unic de referință.

Publicat 30/12/2014 la 14:45
sursa de către utilizator

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