Wydajność Object.GetType ()

głosy
38

Mamy wiele połączeń zalogowaniu naszej aplikacji. Nasz rejestrator przyjmuje parametr System.Type więc może pokazać, który składnik utworzył połączenie. Czasami, kiedy możemy być jedno, robimy coś takiego:

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

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

Jak wymaga tego uzyskanie Rodzaj obiektu tylko raz. Jednak nie mamy żadnych rzeczywiste dane na ten temat. Ktoś ma jakiś pomysł, jak wiele to oszczędność ponad nazywając this.GetType () za każdym razem logować?

(Zdaję sobie sprawę, mogę zrobić sobie z metryk nie jest duży problem, ale hej, co jest dla StackOverflow?)

Utwórz 09/12/2008 o 17:20
źródło użytkownik
W innych językach...                            


7 odpowiedzi

głosy
70

I podejrzewam, że GetType () zajmie znacznie mniej czasu niż rzeczywisty logowania. Oczywiście, istnieje możliwość, że wezwanie do Logger.Log nie zrobi żadnej rzeczywistej IO ... I nadal podejrzewasz, że różnica będzie nieistotna chociaż.

EDIT: Kod Benchmark jest na dole. wyniki:

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

To się wywołując metodę 100 milionów razy - zyski optymalizacji kilka sekund lub tak. Podejrzewam prawdziwa metoda rejestrowania będzie miał dużo więcej do zrobienia, i nazywając że 100 milionów razy potrwa o wiele dłużej niż 4 sekundy, w sumie, nawet jeśli nic nie pisać. (Mogę się mylić, oczywiście - trzeba by spróbować samemu.)

Innymi słowy, jak zwykle, pójdę z najbardziej czytelnego kodu niż mikro optymalizacji.

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;
    }
}
Odpowiedział 09/12/2008 o 17:45
źródło użytkownik

głosy
15

GetType()Funkcja oznaczona jest specjalnym atrybutem [MethodImpl(MethodImplOptions.InternalCall)]. Oznacza to jego ciało metoda nie zawiera IL lecz jest hak do wewnętrznych części CLR .NET. W tym przypadku, to wygląda na binarnej struktury metadanych obiektu i konstruuje System.Typeobiekt wokół niego.

EDIT: Chyba myliłem o czymś ...

I powiedział, że: „ponieważ GetType()wymaga nowego obiektu należy budować”, ale wydaje się, nie jest to poprawne. Jakoś CLR buforuje Typei zawsze wraca do tego samego obiektu, więc nie trzeba budować nowy typ obiektu.

Mam na podstawie następującego testu:

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

Więc nie oczekuj wiele korzyści w swojej realizacji.

Odpowiedział 09/12/2008 o 17:46
źródło użytkownik

głosy
7

Wątpię, masz zamiar dostać satysfakcjonującą odpowiedź od SO na ten temat. Wynika to z faktu, że wydajność, zwłaszcza scenariusze tego typu, są wysoce specyficzne aplikacje.

Ktoś może zakładać z powrotem z szybkim stoper przykładem, który byłby szybszy pod względem surowców milisekund. Ale szczerze, że nic nie znaczy dla danej aplikacji. Czemu? To zależy silnie od sposobu użytkowania wokół tego konkretnego scenariusza. Na przykład ...

  1. Ile rodzajów masz?
  2. Jak duże są ci metody?
  3. Czy to dla każdej metody lub tylko tych dużych?

Są to tylko niektóre z pytań, które w znacznym stopniu zmieniają trafność prostej odniesienia czasowego.

Odpowiedział 09/12/2008 o 17:46
źródło użytkownik

głosy
2

Różnica jest chyba znikome o ile wydajność aplikacji jest zaniepokojony. Ale pierwsze podejście, w którym cache typu powinny być szybsze. Chodźmy i testy.

Ten kod pokaże różnicę:

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 moim komputerze, to dało wyniki ok. 1500 milisekund dla pierwszego podejścia i ok. 2200 milisekund dla drugiego.

(Kod i poprawione czasy - doh!)

Odpowiedział 09/12/2008 o 17:47
źródło użytkownik

głosy
0

Czy można uznać za pomocą nameof operatora?

Odpowiedział 11/09/2017 o 06:42
źródło użytkownik

głosy
0

stosując pole jest najlepszym sposobem i uniknąć wewnętrzną blokadę słownika powodując przez typeof () i GetType (), aby zachować unikalny odniesienia.

Odpowiedział 30/12/2014 o 14:45
źródło użytkownik

głosy
-1

Otrzymuję bardzo różne wyniki.
Do tego stworzył nową aplikację konsoli w innym projekcie, a stosowane klasy z dziedziczenia.

Stworzyłem pustą pętlę do odstąpienia od wyników, dla czystej porównania.
Utworzony const i statyczny dla cykli (ręcznego przełączania, które do stosowania).
Coś bardzo interesującego wydarzyło.

Przy użyciu const, pusty pętli się wolno, ale buforowane Test var się nieco szybciej.
Zmiana, która powinna wpłynąć Brak lub wszystkie testy, tylko wpływa 2.

Cykle dla każdego testu: 100000000

Korzystanie cykl statyczną:

Object.GetType: 1316
Typeof (klasa): 1589
Rodzaj var: 987
Puste Loop: 799

Czysty opis:
Object.GetType: 517
Typeof (klasa): 790
Rodzaj var: 188

Korzystanie const cyklu:

Object.GetType: 1316
Typeof (klasa): 1583
Rodzaj var: 853
Puste Loop: 1061

Czysty opis:
Object.GetType: 255
Typeof (klasa): 522
Rodzaj var: -208

Pobiegłem te kilka razy, i, z niewielkimi zmianami, oraz z 10 razy więcej cykli, aby zmniejszyć ryzyko procesów wpływających na tle wyników. Prawie takie same wyniki jak ci 2 powyżej.

Wydaje się, że Object.GetType()jest 1,5-2 razy szybciej typeof(class).
Buforowana var wydają się być 1,5-2 razy szybciej Object.GetType().

I prawo aplikacja, to nie jest tylko mikro optymalizacji.
Jeśli poświęcić małe rzeczy tu i tam, będą łatwo zwolnić więcej niż jednej big thing dokonanego 30% szybciej.

Ponownie odpowiedział JaredPar, to rodzaj testów, jest zawodna za mówienie o konkretnych zastosowań, jak dowiodły tutaj.
Wszystkie nasze testy dające zupełnie różne wyniki i rzeczy, pozornie niepowiązanych z kodem pod ręką, może mieć wpływ na wydajność.

Test:

.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;
        }
    }
}
Odpowiedział 23/04/2019 o 03:32
źródło użytkownik

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