Тест отражения не показывает ожидаемые числа - PullRequest
5 голосов
/ 09 марта 2011

Я написал тестовый код для сравнения производительности использования прямого доступа к свойству или отражения или отражения с использованием делегатов. Но результаты, которые я получаю, сбивают с толку, поскольку они показывают, что рефлексия не намного медленнее (~ 4%), чем прямой доступ к собственности, что я не считаю верным. Может ли кто-нибудь сказать мне, если я делаю что-то здесь не так?


для 5000 наименований Я получаю следующие результаты

  • прямой доступ: 32,2609 с
  • отражение: 33,623 с отражение
  • с использованием делегатов: 31,7981 с

Код:

private static Random random = new Random((int)DateTime.Now.Ticks);
Private Dictionary<string, Delegate> delegateList = new Dictionary<string, Delegate>(); 
private List<ReflectClass1> dataList = new List<ReflectClass1>();

    private void TestMethod2<T>()
    {
        foreach (var propertyInfo in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (propertyInfo.PropertyType.BaseType != typeof(ValueType))
            {
                Func<T, object> getPropDelegate =
                    (Func<T, object>) Delegate.CreateDelegate(typeof (Func<T, object>), null, propertyInfo.GetGetMethod());
                delegateList.Add(propertyInfo.Name, getPropDelegate);
            }
            //else
            //{
            //    Type propertyType = propertyInfo.PropertyType.GetType();
            //    delegateList.Add(propertyInfo.Name,
            //                     Delegate.CreateDelegate(typeof(Func<T, TResult>), null, propertyInfo.GetGetMethod()));
            //}
        }
    }
    //http:_//stackoverflow.com/questions/1122483/c-random-string-generator     
    private string RandomString(int size)
    {
        StringBuilder builder = new StringBuilder();
        char ch;
        for (int i = 0; i < size; i++)
        {
            ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
            builder.Append(ch);
        }

        return builder.ToString();
    }

    private void SetUpReflectObjList()
    {
        for (int i = 0; i < 5000 ; i++)
        {
            ReflectClass1 reflectClass1 = new ReflectClass1();
            reflectClass1.Prop1 = RandomString(15);
            reflectClass1.Prop2 = RandomString(10);
            reflectClass1.Prop3 = RandomString(10);
            reflectClass1.Prop4 = RandomString(10);
            reflectClass1.Prop5 = RandomString(10);
            reflectClass1.Prop6 = RandomString(10);
            reflectClass1.Prop7 = RandomString(10);
            reflectClass1.Prop8 = RandomString(10);
            reflectClass1.Prop9 = RandomString(10);
            reflectClass1.Prop10 = RandomString(10);
            dataList.Add(reflectClass1);
        }
    }

    private void UseDelegateList()
    {
        Debug.WriteLine(string.Format(" Begin delegate performance test. item count = {0} start time: {1}",dataList.Count, DateTime.Now.ToLongTimeString()));
        for (int i = 0; i < dataList.Count; i++)
        {
            foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties())
            {
                if (delegateList.ContainsKey(propertyInfo.Name))
                {
                    Func<ReflectClass1, object> getPropDelegate = (Func<ReflectClass1, object>) delegateList[propertyInfo.Name];
                    Debug.Write(string.Format(" By delegates Object: {0} Property: {1} Value: {2}", i, propertyInfo.Name, getPropDelegate(dataList[i])));
                }
            }
        }
        Debug.WriteLine("");
        Debug.WriteLine(string.Format(" End delegate performance test. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
    }

    private void UseDirectReflection()
    {
        Debug.WriteLine(string.Format(" Begin direct reflection performance test. item count = {0} start time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
        for (int i = 0; i < dataList.Count; i++)
        {
            foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties())
            {
                if (propertyInfo == null) continue;
                {
                    Debug.Write(string.Format(" By reflection Object: {0} Property: {1} Value: {2}", i, propertyInfo.Name, propertyInfo.GetValue(dataList[i], null)));
                }
            }
        }
        Debug.WriteLine("");
        Debug.WriteLine(string.Format(" End direct reflection performance test. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
    }

    private void DirectOutputTest()
    {
        Debug.WriteLine(string.Format(" Begin direct output benchmark. item count = {0} start time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
        for (int i = 0; i < dataList.Count; i++)
        {

            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop1", dataList[i].Prop1));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop2", dataList[i].Prop2));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop3", dataList[i].Prop3));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop4", dataList[i].Prop4));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop5", dataList[i].Prop5));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop6", dataList[i].Prop6));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop7", dataList[i].Prop7));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop8", dataList[i].Prop8));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop9", dataList[i].Prop9));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop10", dataList[i].Prop10));
        }
        Debug.WriteLine("");
        Debug.WriteLine(string.Format(" End direct output benchmark. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
    }

Ответы [ 3 ]

4 голосов
/ 09 марта 2011

2 вещи:

  • Производительность рефлексии значительно улучшилась в новых средах выполнения, потому что это основная мощь языков .NET и потому, что так много внимания было уделено разнице между статической и динамической производительностью. Я предполагаю, что вы используете это в Framework v3.5 или 4.0; Если бы вы выполняли этот код в Framework v2.0, он, вероятно, работал бы хуже.

  • Большая часть того, что вы делаете, - это не очень «интенсивное» использование отражения. Динамический вызов свойств довольно сложен, но большая часть того, что вы делаете, - это просто получение информации. Настоящие серьезные проблемы - это динамический вызов метода и создание динамического экземпляра.

Скажите, что вы выполнили следующий тест. Очень просто, и единственное, что отличается, это статическое создание и вызов по сравнению с рефлексивным:

public class ReflectionTest
    {
        public int Method1(){return 1;}
        public int Method2() { return 2; }
        public int Method3() { return 3; }
        public int Method4() { return 4; }
        public int Method5() { return 5; }
        public int Method6() { return 6; }
    }

    [Test]
    public void TestStatic()
    {
        for (var i = 1; i <= 100000; i++)
        {
            var reflectTest = new ReflectionTest();
            reflectTest.Method1();
            reflectTest.Method2();
            reflectTest.Method3();
            reflectTest.Method4();
            reflectTest.Method5();
            reflectTest.Method6();
        }
    }

    [Test]
    public void TestReflection()
    {
        var fullName = typeof (ReflectionTest).FullName;
        for (var i = 1; i <= 100000; i++)
        {
            var type = Assembly.GetExecutingAssembly().GetType(fullName, true, true);
            var reflectTest = Activator.CreateInstance(type);
            for (var j = 1; j <= 6; j++)
                type.GetMethod("Method" + j.ToString()).Invoke(reflectTest, null);
        }
    }

Если вы хотите убедиться, что тестирование было полностью честным, вы можете удалить внутренний цикл for и вызвать GetMethod 6 раз со строковыми литералами «Method1», «Method2» и т. Д.

Тест отражения не только динамически вызывает методы, он выполняет поиск манифеста для поиска и создания экземпляра объекта Type, а затем динамически создает экземпляр реального объекта из Type, для которого методы динамически вызываются. Могу поспорить, что если вы запустите оба теста, второй будет работать намного хуже. Также изучите параметры передачи этих методов; Сначала вы должны найти правильную перегрузку, затем рефлексивный вызов принимает массив Object [], который будет блокировать и распаковывать любые значения-значения параметров метода (ов), еще больше замедляя алгоритм рефлексии.

Короче говоря, отражение будет работать хуже, чем статический алгоритм; ОДНАКО были достигнуты большие успехи в улучшении его производительности, поэтому в .NET 4.0 интеллектуально написанный динамический алгоритм не такой большой убыток по сравнению с аналогичным статическим алгоритмом, что делает отражение гораздо более жизнеспособным при необходимости.

РЕДАКТИРОВАТЬ: После выполнения двух вышеуказанных тестов, есть большая относительная разница: статический алгоритм 0,07 с для 100 000 итераций, отражающий колоссальные 2,12 с. Рефлексивная реализация / вызов занимает в 30 раз больше времени, чем статическая. Однако разница в 100 000 итераций была значительной; операторы Debug.WriteLine в моем первоначальном воплощении этого теста были, безусловно, самой медленной частью любого теста.

3 голосов
/ 09 марта 2011

Я переписал ваш тест, чтобы просто получить доступ к свойству во внутреннем цикле и не использовать список вещей, и получил следующие результаты:

Отладка:

  • Завершение теста производительности делегата,итерации = 1 000 000: время окончания: 0,278
  • Завершение теста производительности прямого отражения.итерации = 1 000 000 конечное время: 5,622
  • конечный тест прямого вывода.итерации = 1 000 000 время окончания: 0,045

Выпуск:

  • Завершение теста производительности делегата.итерации = 1 000 000: время окончания: 0,194
  • Завершение теста производительности прямого отражения.итерации = 1 000 000 конечное время: 5,523
  • конечный тест прямого вывода.итерации = 1 000 000 конечное время: 0,003

Конечно, если вы не обращаетесь к свойству миллионы раз, имеет ли это влияние на производительность для вашего приложения?

class ReflectClass1
{
    public ReflectClass1(Random rand)
    {
        Prop1 = rand.Next();
        Prop2 = rand.Next();
        Prop3 = rand.Next();
        Prop4 = rand.Next();
        Prop5 = rand.Next();
        Prop6 = rand.Next();
        Prop7 = rand.Next();
        Prop8 = rand.Next();
        Prop9 = rand.Next();
        Prop10 = rand.Next();
    }

    public int Prop1 {get;set;}
    public int Prop2 { get; set; }
    public int Prop3 { get; set; }
    public int Prop4 { get; set; }
    public int Prop5 { get; set; }
    public int Prop6 { get; set; }
    public int Prop7 { get; set; }
    public int Prop8 { get; set; }
    public int Prop9 { get; set; }
    public int Prop10 { get; set; }
}

class Program
{
    private static Random random = new Random((int)DateTime.Now.Ticks);
    private List<Func<ReflectClass1, int>> delegateList = new List<Func<ReflectClass1, int>>();
    private static int Iterations = 1000000;

    private void UseDelegateList()
    {
        //setup delegateList
        foreach (var propertyInfo in typeof(ReflectClass1).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            Func<ReflectClass1, int> getPropDelegate = (Func<ReflectClass1, int>)Delegate.CreateDelegate(typeof(Func<ReflectClass1, int>), null, propertyInfo.GetGetMethod());
            delegateList.Add(getPropDelegate);
        }

        Stopwatch sw = Stopwatch.StartNew();

        ReflectClass1 testClass = new ReflectClass1(random);
        int total = 0;
        for (int i = 0; i < Iterations; i++)
        {
            foreach (var getProp in delegateList)
            {
                total += getProp(testClass);
            }           
        }

        Console.WriteLine(string.Format(" End delegate performance test. iterations = {0:N0} end time: {1:0.000}", Iterations, sw.ElapsedMilliseconds / 1000.0));
        Console.WriteLine(total);
    }

    private void UseDirectReflection()
    {
        Stopwatch sw = Stopwatch.StartNew();

        int total = 0;
        ReflectClass1 testClass = new ReflectClass1(random);
        for (int i = 0; i < Iterations; i++)
        {
            foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties())
            {
                if (propertyInfo == null)
                    continue;

                total += (int)propertyInfo.GetValue(testClass, null);
            }
        }

        Console.WriteLine(string.Format(" End direct reflection performance test. iterations = {0:N0} end time: {1:0.000}", Iterations, sw.ElapsedMilliseconds / 1000.0));
        Console.WriteLine(total);
    }

    private void DirectOutputTest()
    {
        Stopwatch sw = Stopwatch.StartNew();

        int total = 0;
        ReflectClass1 testClass = new ReflectClass1(random);
        for (int i = 0; i < Iterations; i++)
        {
            total += testClass.Prop1;
            total += testClass.Prop2;
            total += testClass.Prop3;
            total += testClass.Prop4;
            total += testClass.Prop5;
            total += testClass.Prop6;
            total += testClass.Prop7;
            total += testClass.Prop8;
            total += testClass.Prop9;
            total += testClass.Prop10;
        }


        Console.WriteLine(string.Format(" End direct output benchmark. iterations = {0:N0} end time: {1:0.000}", Iterations, sw.ElapsedMilliseconds / 1000.0));
        Console.WriteLine(total);
    }

    static void Main(string[] args)
    {
        var test = new Program();

        test.UseDelegateList();
        test.UseDirectReflection();
        test.DirectOutputTest();
    }
}
3 голосов
/ 09 марта 2011

Это правда.Отражение обычно медленное в релятивистских терминах, но обычно не медленное в повседневной жизни, когда стоит беспокоиться о терминах.Вот отличная статья о медленных частях отражения и не очень медленных частях: Статья об отражении

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...