Performance von Object.GetType ()

stimmen
38

Wir haben viele Protokollierung Anrufe in unserer App. Unser Logger nimmt einen System.Type Parameter so es welche Komponente erstellt den Anruf zeigen. Manchmal, wenn wir gestört werden können, tun wir so etwas wie:

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

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

Da dies erfordert nur ein einziges Mal das Type-Objekt zu bekommen. Allerdings haben wir keine aktuellen Messwerte auf diesem. Jeder bekam eine Ahnung, wie viel das spart über this.GetType () jedes Mal rufen wir anmelden?

(Ich weiß, ich könnte die Metriken selbst ohne großes Problem, aber hey, was ist für Stackoverflow?)

Veröffentlicht am 09/12/2008 um 17:20
quelle vom benutzer
In anderen Sprachen...                            


7 antworten

stimmen
70

Ich vermute stark, dass GetType () deutlich weniger Zeit als jede tatsächliche Protokollierung statt. Natürlich gibt es die Möglichkeit, dass Ihr Anruf Logger.log wird eine tatsächliche IO nicht tun ... Ich vermute immer noch der Unterschied jedoch irrelevant sein wird.

EDIT: Benchmark-Code ist an der Unterseite. Ergebnisse:

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

Das ruft das Verfahren 100 Mio. Mal - die Optimierungsgewinne ein paar Sekunden oder so. Ich vermute , dass die reale Protokollierungsmethode viel mehr Arbeit zu tun hat, und fordere , dass 100 Millionen Mal viel länger als 4 Sekunden insgesamt dauern wird, auch wenn er nichts schreiben. (Ich könnte falsch sein, natürlich -, dass selbst versuchen müssten.)

Mit anderen Worten, wie normal, würde ich mit den meisten lesbaren Code gehen, anstatt Mikro-Optimierung.

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;
    }
}
Beantwortet am 09/12/2008 um 17:45
quelle vom benutzer

stimmen
15

Die GetType()Funktion wird mit dem speziellen Attribute gekennzeichnet [MethodImpl(MethodImplOptions.InternalCall)]. Dies bedeutet , das Verfahren zu Körper nicht IL enthalten , sondern stattdessen ein Haken in die Interna der .NET CLR ist. In diesem Fall sieht es auf der binären Struktur der Metadaten des Objekts und erstellt ein System.TypeObjekt um ihn herum.

EDIT: Ich glaube , ich war falsch über etwas ...

Ich sagte , dass: „weil GetType()ein neues Objekt erfordert werden bauen“ , aber es scheint dies nicht korrekt ist. Irgendwie Caches die CLR die Typeund immer wieder das gleiche Objekt , so dass es keinen neuen Type - Objekt zu bauen braucht.

Ich bin auf der folgenden Prüfung zugrunde gelegt:

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

Also, ich erwarte nicht viel Gewinn in Ihrer Implementierung.

Beantwortet am 09/12/2008 um 17:46
quelle vom benutzer

stimmen
7

Ich bezweifle, dass Sie gehen, um von SO zu diesem Thema eine befriedigende Antwort zu erhalten. Der Grund, dass die Leistung zu sein, vor allem Szenarien dieser Art ist sehr anwendungsspezifisch.

Jemand kann mit einem schnellen Stoppuhr Beispiel davon wäre schneller in Bezug auf die rohen miliseconds Post zurück. Aber ehrlich gesagt bedeutet das nichts für Ihre Anwendung. Warum? Es hängt stark von den Nutzungsmustern um dieses spezielle Szenario. Zum Beispiel ...

  1. Wie viele Arten haben Sie?
  2. Wie groß sind Sie Methoden?
  3. Haben Sie für jede Methode tun dies, oder nur Großen?

Dies sind nur einige der Fragen, die sich stark um die Relevanz einer geraden Zeit Benchmark verändern wird.

Beantwortet am 09/12/2008 um 17:46
quelle vom benutzer

stimmen
2

Der Unterschied ist wahrscheinlich vernachlässigbar, soweit die Anwendungsleistung angeht. Aber der erste Ansatz, bei dem Sie den Typ-Cache sollte schneller sein. Lassen Sie uns gehen und zu testen.

Dieser Code zeigt Ihnen den Unterschied:

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

Auf meinem Rechner gab diese Ergebnisse von rd. 1500 Millisekunden für den ersten Ansatz und ca.. 2200 Millisekunden, für die zweite.

(Code und Timings korrigiert - Doh!)

Beantwortet am 09/12/2008 um 17:47
quelle vom benutzer

stimmen
0

unter Verwendung von Feld ist der beste Weg, und es interne Wörterbuch Sperre vermeiden durch typeof verursacht () und GetType () eindeutige Referenz zu halten.

Beantwortet am 30/12/2014 um 14:45
quelle vom benutzer

stimmen
0

Haben Sie darüber nachgedacht mit nameof Operator?

Beantwortet am 11/09/2017 um 06:42
quelle vom benutzer

stimmen
-1

Ich bekomme sehr unterschiedliche Ergebnisse.
Dazu habe ich eine neue Konsolenanwendung in einem anderen Projekt, und verwendet , um eine Klasse mit Vererbung.

Ich habe eine leere Schleife aus den Ergebnissen zu ziehen, für einen sauberen Vergleich.
Ich habe eine const und eine statische für die Zyklen (manuell Schalt was zu verwenden).
Etwas sehr interessant passiert.

Wenn die const verwendet, wurde die leere Schleife langsam, aber der gepufferte var Test wird etwas schneller.
Eine Änderung, die sollte keine oder alle Tests beeinflussen, wirken sich nur auf 2

Zyklen für jeden Test: 100000000 Die

Verwendung von statischen Zyklus:

Object.GetType: 1316
TypeOf (Class): 1589
Typ var: 987
Leere Loop: 799

Saubere Überblick:
Object.GetType: 517
TypeOf (Class): 790
Typ var: 188

Mit konst Zyklus:

Object.GetType: 1316
TypeOf (Class): 1583
Typ var: 853
Leere Loop: 1061

Saubere Überblick:
Object.GetType: 255
TypeOf (Klasse): 522
Typ var: -208

Ich lief diese mehrere Male, und mit einigen kleinen Änderungen, und mit 10 mal mehr Zyklen, um das Risiko von Hintergrundprozessen zu beeinflussen Ergebnisse zu reduzieren. Fast dieselben Ergebnisse wie diese 2 oben.

Es scheint , dass Object.GetType()1,5-2 mal so schnell wie typeof(class).
Die gepufferte var scheinen 1,5-2 mal so groß wie schnell zu sein Object.GetType().

Ich die richtige Anwendung, ist dies nicht nur Mikro-Optimierung.
Wenn Sie kleine Dinge hier und da opfern, werden sie langsam leicht mehr als die eine große Sache , die Sie 30% schneller gemacht.

Wieder wie JaredPar beantwortet, ist diese Art von Tests unzuverlässig über Ihre spezifische Anwendung zu sagen, wie wir es hier unter Beweis gestellt haben.
Alle unsere Tests recht unterschiedliche Ergebnisse geben, und die Dinge scheinbar ohne Bezug auf den Code zur Hand, kann die Leistung beeinträchtigen.

Der 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;
        }
    }
}
Beantwortet am 23/04/2019 um 03:32
quelle vom benutzer

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