Getting data through reflection; GetValue

In C#/.NET, you can get data from an object using reflection which can be useful for things like mapping and testing. Typically, you’d use direct access, but reflection allows you to get data from fields and properties that you were un-aware of at compile time. This can save you a lot of code. But what are the performance implications?

Turns out that getting data directly from a property or field is 50-80 times faster than using the “GetValue” method. The code to create and access the delegate looks like this;

            ClassWithProperty cwp = new ClassWithProperty();

            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            MethodInfo method = propertyInfo.GetAccessors().First();

            Func<ClassWithProperty, List<string>> dlg =
                (Func<ClassWithProperty, List<string>>)
                Delegate.CreateDelegate(typeof(Func<ClassWithProperty, List<string>>), method);

            List<string> list = dlg(cwp);

For properties, you can also create a delegate that calls the setter method directly, which is plenty fast. This method can’t be used for fields though.

Note that the “Reflection Field Accessor” is way faster (6.3 times) than the “Reflection Property Accessor”! So accessing fields through reflection is much faster than accessing properties through reflection, but both are much slower than direct access.

Test type Time Iterations/s Time/iter Difference
Direct Property Accessor 36 ms 138.888.888 7,2 ns Baseline
Delegate Property Accessor 42 ms 119.047.619 8,4 ns 1.16 times slower
Reflection Property Accessor 6098 ms 819.940 1219 ns 107 times slower
Direct Field Accessor 15 ms 333.333.333 3 ns Baseline
Reflection Field Accessor 953 ms 5.246.589 190 ns 59 times slower

 

Here’s the actual code for performing these tests;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 5000000;

                Console.WriteLine("Comparing Property Get Methods");
                long direct = TestDirectPropertyAccess(iterations);
                long delegatea = TestDelegatePropertyAccess(iterations);
                long reflection = TestReflectionPropertyAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    1.0 * reflection / direct);
                Console.WriteLine(
                    "Direct access is {0} times faster than delegate access",
                    1.0 * delegatea / direct);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Comparing Field Access Methods");
                direct = TestDirectFieldAccess(iterations);
                reflection = TestReflectionFieldAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestDirectPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = cwp.MyProperty;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDelegatePropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();

            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            MethodInfo method = propertyInfo.GetAccessors().First();

            Func<ClassWithProperty, List<string>> dlg =
                (Func<ClassWithProperty, List<string>>)
                Delegate.CreateDelegate(typeof(Func<ClassWithProperty, List<string>>), method);


            Console.WriteLine("Reflection Property Accessor...");
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = dlg(cwp);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = (List<string>)propertyInfo.GetValue(cwp, emptyArray);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDirectFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = cwp.MyField;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            FieldInfo fieldInfo =
                typeof(ClassWithProperty)
                    .GetFields()
                    .Single(prop => prop.Name == "MyField");

            Console.WriteLine("Reflection Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = (List<string>)fieldInfo.GetValue(cwp);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }
    }

    public class ClassWithProperty
    {
        public ClassWithProperty()
        {
            MyProperty = new List<string>();
            MyField = new List<string>();
        }

        // I know, totally wrong naming and everything...
        public List<string> MyField;
        public List<string> MyProperty { get; set; }
    }
}

About mfagerlund
Writes code in my sleep - and sometimes it even compiles!

2 Responses to Getting data through reflection; GetValue

  1. Pingback: Reflection, Type.GetProperties and performance « Mattias Fagerlund's Coding Blog

  2. Nick says:

    Thank you! Brilliant research.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: