Выполнение Object.GetType ()

голоса
38

У нас есть много лесозаготовительных вызовов в нашем приложении. Наш регистратор принимает параметр System.Type поэтому он может показать, какой компонент создал вызов. Иногда, когда мы можем быть обеспокоены, что мы делаем что-то вроде:

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

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

Поскольку это требует получения объекта типа только один раз. Однако мы не имеем каких-либо фактических показателей по этому вопросу. Кто есть какие-либо идеи, сколько это экономит над вызовом this.GetType () каждый раз, когда мы зайдем?

(Я понимаю, что я мог сделать метрики себя с большой проблемой, но эй, что StackOverflow для?)

Задан 09/12/2008 в 17:20
источник пользователем
На других языках...                            


7 ответов

голоса
70

Я сильно подозреваю, что GetType () будет иметь значительно меньше времени, чем какой-либо фактической регистрации. Конечно, есть вероятность того, что ваш призыв к Logger.log не будет делать каких-либо фактических IO ... Я до сих пор подозреваю, что разница будет иметь никакого значения, хотя.

EDIT: Benchmark код находится в нижней части. Результаты:

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

Это звонит способ 100 миллионов раз - выгоды оптимизации пару секунд или около того . Я подозреваю , что реальный метод регистрации будет иметь гораздо больше работы, и призывая 100 миллионов раз займут гораздо больше времени , чем за 4 секунды , в общей сложности, даже если он ничего не выписывает. (Я могу ошибаться, конечно , - вы должны попробовать это сами.)

Другими слова, как обычно, я бы с наиболее читаемым кодом, а не микро-оптимизацией.

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;
    }
}
Ответил 09/12/2008 в 17:45
источник пользователем

голоса
15

GetType()Функция помечается специальным атрибутом [MethodImpl(MethodImplOptions.InternalCall)]. Это означает , что метод его тело не содержит IL , но вместо того, чтобы хук в внутренности .NET CLR. В этом случае, он смотрит на бинарную структуру метаданных объекта и создает System.Typeобъект вокруг него.

EDIT: Я думаю , что я был неправ о чем - то ...

Я сказал , что: «потому , что GetType()требует нового объекта , который будет строить» , но мне кажется это не правильно. Так или иначе, CLR кэширует Typeи всегда возвращает один и тот же объект , так что не нужно строить новый объект типа.

Я основан на следующем испытании:

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

Таким образом, я не ожидаю большого выигрыша в вашей реализации.

Ответил 09/12/2008 в 17:46
источник пользователем

голоса
7

Я сомневаюсь, что вы собираетесь получить удовлетворительный ответ от SO по этому вопросу. Причина в том, что производительность, особенно сценарии такого типа, весьма специализированные.

Кто-то может отправлять обратно с быстрым секундомером примером которого будет быстрее с точкой зрения сырья миллисекунды. Но, честно говоря, что ничего для вашего приложения не значит. Зачем? Это сильно зависит от схемы использования вокруг этого конкретного сценария. Например ...

  1. Сколько видов у вас есть?
  2. Насколько велики вам методы?
  3. Вы делаете это для каждого метода или только больших,?

Это лишь немногие из вопросов, которые значительно изменяют актуальность прямого времени отсчета.

Ответил 09/12/2008 в 17:46
источник пользователем

голоса
2

Разница, вероятно, ничтожна, насколько производительность приложений обеспокоена. Но первый подход, при котором вы кэшировать тип должен быть быстрее. Давайте и тест.

Этот код покажет вам разницу:

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

На моей машине, это дало результаты ок. 1500 миллисекунд для первого подхода и ок. 2200 миллисекунд для второго.

(Код и тайминги исправлен - DOH!)

Ответил 09/12/2008 в 17:47
источник пользователем

голоса
0

Рассматривали ли вы с помощью nameof оператора?

Ответил 11/09/2017 в 06:42
источник пользователем

голоса
0

с использованием поля является лучшим способом, и избежать внутреннего словаря замка причинения TYPEOF () и GetType (), чтобы сохранить уникальную ссылку.

Ответил 30/12/2014 в 14:45
источник пользователем

голоса
-1

Я получаю очень разные результаты.
Для этого я создал новый консольного приложения в другом проекте, и используется класс с наследованием.

Я создал пустой цикл , чтобы вывести из результатов, для чистого сравнения.
Я создал константный и статическую для циклов (ручное переключения для использования).
Что - то очень интересное случилось.

При использовании сопзЬ, пустой цикл становится медленным, но буферном тест вар становится немного быстрее.
Изменение , которое должно повлиять ни один или все тесты, влияют только на 2.

Циклы для каждого теста: 100000000

Использование статического цикла:

Object.GetType: 1316
TypeOf (класс): 1589
Тип вар: 987
Empty Loop: 799

Чистый обзор:
Object.GetType: 517
TypeOf (класс): 790
Тип вар: 188

Использование константного цикла:

Object.GetType: 1316
TypeOf (класс): 1583
Тип вар: 853
Пустой цикл: 1061

Чистый обзор:
Object.GetType: 255
TypeOf (класс): 522
Тип вар: -208

Я побежал эти несколько раз, и с некоторыми небольшими изменениями, и в 10 раз больше циклов, чтобы уменьшить риск фоновых процессов , влияющих на результаты. Почти такие же результаты , как эти 2 выше.

Похоже , что Object.GetType()в 1,5-2 раза быстрее, чем typeof(class).
Буферном вар , как представляется, в 1,5-2 раза быстрее, чем Object.GetType().

Я правильное применение, это не только микро-оптимизации.
Если вы пожертвовать небольшие вещи здесь и там, они будут легко замедлить больше , чем одна большая вещь , которую вы сделали 30% быстрее.

Опять же, как ответил JaredPar, такого рода испытания, ненадежен для рассказывающих о конкретном области применения, как мы доказали здесь.
Все наши тесты , дающие совершенно разные результаты, и вещи , казалось бы , не связанные с кодом под руку, могут повлиять на производительность.

Тест:

.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;
        }
    }
}
Ответил 23/04/2019 в 03:32
источник пользователем

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