开发者

Reflection test does not show expected numbers

I have written some test code for comparing the performance of using direct property access or reflection or reflection by using delegates. But the results that I get are baffling, since it is showing that reflection is not much slower (~4%) than direct property access, which I do not believe to be true. Could someone tell me if I am doing something wrong here?


for 5000 items I am getting the below results

  • direct access: 32.2609 secs
  • reflection: 33.623 secs reflection by
  • using delegates: 31.7981 secs

Code:

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])));
                }
   开发者_StackOverflow         }
        }
        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()));
    }


2 things:

  • Performance of reflection has gotten much better in newer runtimes, because it is a major power of .NET languages and because so much attention has been paid to the difference between static and dynamic performance. I assume you're running this in Framework v3.5 or 4.0; If you were to execute this code in the Framework v2.0, it would probably perform more poorly.

  • Most of what you're doing is not very "heavy" use of reflection. Dynamic property invocation is kind of heavy, but most of what you're doing is just getting information. The real heavy-hitters are dynamic method invocation and dynamic instance creation.

Say that you ran the following test. Very simple, and the only thing different is static instantiation and invocation vs reflective:

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

If you wanted to make sure the testing was completely fair, you could remove the inner for loop and call GetMethod 6 times with string literals "Method1", "Method2", etc.

The reflection test not only invokes methods dynamically, it searches the manifest to find and instantiate a Type object, then it dynamically instantiates the actual object from the Type, on which the methods are dynamically called. I would bet that if you ran both tests, the second one would perform much more poorly. Also, explore passing parameters to these methods; First you have to find the right overload, then the reflective invocation takes an Object[] array, which will box and unbox any value-type parameters of the method(s), further slowing the reflective algorithm down.

In short, reflection will perform more poorly than a static algorithm; HOWEVER there have been great strides made to improve its performance, so as of .NET 4.0, an intelligently-written dynamic algorithm is not such a huge loss versus a counterpart static algorithm, making reflection far more viable when needed.

EDIT: After running the above 2 tests side by side, there is a big relative difference: static algorithm 0.07s for 100k iterations, reflective a whopping 2.12s. Reflective instantiation/invocation takes 30 times longer than static. However, the difference took 100,000 iterations to be significant; the Debug.WriteLine statements in my original incarnation of this test were, by far, the slowest part of either test.


I rewrote your test to just access the property in the inner loop and not use a list of things and got the following results:

Debug:

  • End delegate performance test. iterations = 1,000,000 end time: 0.278
  • End direct reflection performance test. iterations = 1,000,000 end time: 5.622
  • End direct output benchmark. iterations = 1,000,000 end time: 0.045

Release:

  • End delegate performance test. iterations = 1,000,000 end time: 0.194
  • End direct reflection performance test. iterations = 1,000,000 end time: 5.523
  • End direct output benchmark. iterations = 1,000,000 end time: 0.003

Of course if you arent accessing the property millions of times then does the performance impact actually matter for your application?

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();
    }
}


Its true. Reflection is usually slow in relativistic terms, but not usually slow in day-to-day worth worrying about terms. Here's a great article on the slow parts of reflection and the not-so-slow parts: Reflection Article

I think worrying about reflection falls into the premature optimization category. If the best way to express your design is to use reflection then use it. Otherwise don't. Also, I'd be careful about running code in an loop that executes just that code and attempting to glean some meaningful numbers. You'll end up micro-optimizing something that really had no impact in your application.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜