Click here to Skip to main content
15,916,431 members
Articles / Reflection.Emit

Why is Reflection Slow?

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
15 Dec 2016CPOL10 min read 20.8K   16   4
Why is Reflection slow?

It’s common knowledge that reflection in .NET is slow, but why is that the case? This post aims to figure that out by looking at what reflection does under-the-hood.

CLR Type System Design Goals

But first it’s worth pointing out that part of the reason reflection isn’t fast is that it was never designed to have high-performance as one of its goals, from Type System Overview - ‘Design Goals and Non-goals’:

Goals

  • Accessing information needed at runtime from executing (non-reflection) code is very fast.
  • Accessing information needed at compilation time for generating code is straightforward.
  • The garbage collector/stackwalker is able to access necessary information without taking locks, or allocating memory.
  • Minimal amounts of types are loaded at a time.
  • Minimal amounts of a given type are loaded at type load time.
  • Type system data structures must be storable in NGEN images.

Non-Goals

  • All information in the metadata is directly reflected in the CLR data structures.
  • All uses of reflection are fast.

and along the same lines, from Type Loader Design - ‘Key Data Structures’:

EEClass

MethodTable data are split into “hot” and “cold” structures to improve working set and cache utilization. MethodTable itself is meant to only store “hot” data that are needed in program steady state. EEClass stores “cold” data that are typically only needed by type loading, JITing or reflection. Each MethodTable points to one EEClass.

How Does Reflection Work?

So we know that ensuring reflection was fast was not a design goal, but what is it doing that takes the extra time?

Well, there several things that are happening. To illustrate this, let's look at the managed and unmanaged code call-stack that a reflection call goes through.

  • System.Reflection.RuntimeMethodInfo.Invoke(..) - Source code link
    • calling System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(..)
  • System.RuntimeMethodHandle.PerformSecurityCheck(..) - link
    • calling System.GC.KeepAlive(..)
  • System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(..) - link
    • calling stub for System.RuntimeMethodHandle.InvokeMethod(..)
  • stub for System.RuntimeMethodHandle.InvokeMethod(..) - link

Even if you don’t click the links and look at the individual C#/CPP methods, you can intuitively tell that there’s a lot of code being executed along the way. But to give you an example, the final method, where the bulk of the work is done, System.RuntimeMethodHandle.InvokeMethod is over 400 LOC!

But this is a nice overview, however what is it specifically doing?

Fetching the Method Information

Before you can invoke a field/property/method via reflection, you have to get the FieldInfo/PropertyInfo/MethodInfo handle for it, using code like this:

C#
Type t = typeof(Person);      
FieldInfo m = t.GetField("Name");

As shown in the previous section, there’s a cost to this, because the relevant meta-data has to be fetched, parsed, etc. Interestingly enough, the runtime helps us by keeping an internal cache of all the fields/properties/methods. This cache is implemented by the RuntimeTypeCache class and one example of its usage is in the RuntimeMethodInfo class.

You can see the cache in action by running the code in this gist, which appropriately enough uses reflection to inspect the runtime internals!

Before you have done any reflection to obtain a FieldInfo, the code in the gist will print this:

Type: ReflectionOverhead.Program
Reflection Type: System.RuntimeType (BaseType: System.Reflection.TypeInfo)
m_fieldInfoCache is null, cache has not been initialised yet

But once you’ve fetched even just one field, then the following will be printed:

Type: ReflectionOverhead.Program
Reflection Type: System.RuntimeType (BaseType: System.Reflection.TypeInfo)
RuntimeTypeCache: System.RuntimeType+RuntimeTypeCache,
m_cacheComplete = True, 4 items in cache
  [0] - Int32 TestField1 - Private
  [1] - System.String TestField2 - Private
  [2] - Int32 <TestProperty1>k__BackingField - Private
  [3] - System.String TestField3 - Private, Static

where ReflectionOverhead.Program looks like this:

C#
class Program
{
    private int TestField1;
    private string TestField2;
    private static string TestField3;

    private int TestProperty1 { get; set; }
}

This means that repeated calls to GetField or GetFields are cheaper as the runtime only has to filter the pre-existing list that’s already been created. The same applies to GetMethod and GetProperty, when you call them the first time the MethodInfo or PropertyInfo cache is built.

Argument Validation and Error Handling

But once you’ve obtained the MethodInfo, there’s still a lot of work to be done when you call Invoke on it. Imagine you wrote some code like this:

C#
PropertyInfo stringLengthField = 
    typeof(string).GetProperty("Length", 
        BindingFlags.Instance | BindingFlags.Public);
var length = stringLengthField.GetGetMethod().Invoke(new Uri(), new object[0]);

If you run it, you would get the following exception:

C#
System.Reflection.TargetException: Object does not match target type.
   at System.Reflection.RuntimeMethodInfo.CheckConsistency(..)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(..)
   at System.Reflection.RuntimeMethodInfo.Invoke(..)
   at System.Reflection.RuntimePropertyInfo.GetValue(..)

This is because we have obtained the PropertyInfo for the Length property on the String class, but invoked it with an Uri object, which is clearly the wrong type!

In addition to this, there also has to be validation of any arguments you pass through to the method you are invoking. To make argument passing work, reflection APIs take a parameter that is an array of objects, one per argument. So if you using reflection to call the method Add(int x, int y), you would invoke it by calling methodInfo.Invoke(.., new [] { 5, 6 }). At run-time, checks need to be carried out on the amount and types of the values passed in, in this case to ensure that there are 2 and that they are both ints. One down-side of all this work is that it often involves boxing which has an additional cost, but hopefully this will be minimised in the future.

Security Checks

The other main task that is happening along the way is multiple security checks. For instance, it turns out that you aren’t allowed to use reflection to call just any method you feel like. There are some restricted or ‘Dangerous Methods’, that can only be called by trusted .NET framework code. In addition to a black-list, there are also dynamic security checks depending on the current Code Access Security permissions that have to be checked during invocation.

How Much Does Reflection Cost?

So now that we know what reflection is doing behind-the-scenes, it’s a good time to look at what it costs us. Please note that these benchmarks are comparing reading/writing a property directly v via reflection. In .NET, properties are actually a pair of Get/Set methods that the compiler generates for us, however when the property has just a simple backing field the .NET JIT inlines the method call for performance reasons. This means that using reflection to access a property will show reflection in the worse possible light, but it was chosen as it’s the most common use-case, showing up in ORMs, Json serialisation/deserialisation libraries and object mapping tools.

Below are the raw results as they are displayed by BenchmarkDotNet, followed by the same results displayed in 2 separate tables. (full Benchmark code is available)

Reflection Benchmark Results

Reading a Property (‘Get’)

MethodMeanStdErrScaledBytes Allocated/Op
GetViaProperty0.2159 ns0.0047 ns1.000.00
GetViaDelegate1.8903 ns0.0082 ns8.820.00
GetViaILEmit2.9236 ns0.0067 ns13.640.00
GetViaCompiledExpressionTrees12.3623 ns0.0200 ns57.650.00
GetViaFastMember35.9199 ns0.0528 ns167.520.00
GetViaReflectionWithCaching125.3878 ns0.2017 ns584.780.00
GetViaReflection197.9258 ns0.2704 ns923.080.01
GetViaDelegateDynamicInvoke842.9131 ns1.2649 ns3,931.17419.04

Writing a Property (‘Set’)

MethodMeanStdErrScaledBytes Allocated/Op
SetViaProperty1.4043 ns0.0200 ns6.550.00
SetViaDelegate2.8215 ns0.0078 ns13.160.00
SetViaILEmit2.8226 ns0.0061 ns13.160.00
SetViaCompiledExpressionTrees10.7329 ns0.0221 ns50.060.00
SetViaFastMember36.6210 ns0.0393 ns170.790.00
SetViaReflectionWithCaching214.4321 ns0.3122 ns1,000.0798.49
SetViaReflection287.1039 ns0.3288 ns1,338.99115.63
SetViaDelegateDynamicInvoke922.4618 ns2.9192 ns4,302.17390.99

So we can clearly see that regular reflection code (GetViaReflection and SetViaReflection) is considerably slower than accessing the property directly (GetViaProperty and SetViaProperty). But what about the other results, let's explore those in more detail.

Setup

First, we start with a TestClass that looks like this:

C#
public class TestClass
{
    public TestClass(String data)
    {
        Data = data;
    }

    private string data;
    private string Data
    {
        get { return data; }
        set { data = value; }
    }
}

and the following common code, that all the options can make use of:

C#
// Setup code, done only once 
TestClass testClass = new TestClass("A String");
Type @class = testClass.GetType();
BindingFlag bindingFlags = BindingFlags.Instance | 
                           BindingFlags.NonPublic | 
                           BindingFlags.Public;

Regular Reflection

First, we use regular benchmark code, that acts as our starting point and the ‘worst case’:

C#
[Benchmark]
public string GetViaReflection()
{
    PropertyInfo property = @class.GetProperty("Data", bindingFlags);
    return (string)property.GetValue(testClass, null);
}

Option 1 - Cache PropertyInfo

Next up, we can gain a small speed boost by keeping a reference to the PropertyInfo, rather than fetching it each time. But we’re still much slower than accessing the property directly, which demonstrates that there is a considerable cost in the ‘invocation’ part of reflection.

C#
// Setup code, done only once
PropertyInfo cachedPropertyInfo = @class.GetProperty("Data", bindingFlags);

[Benchmark]
public string GetViaReflection()
{    
    return (string)cachedPropertyInfo.GetValue(testClass, null);
}

Option 2 - Use FastMember

Here we make use of Marc Gravell’s excellent Fast Member library, which as you can see is very simple to use!

C#
// Setup code, done only once
TypeAccessor accessor = TypeAccessor.Create(@class, allowNonPublicAccessors: true);

[Benchmark]
public string GetViaFastMember()
{
    return (string)accessor[testClass, "Data"];
}

Note that it’s doing something slighty different to the other options. It creates a TypeAccessor that allows access to all the Properties on a type, not just one. But the downside is that, as a result, it takes longer to run. This is because internally it first has to get the delegate for the Property you requested (in this case ‘Data’), before fetching its value. However, this overhead is pretty small, FastMember is still way faster than Reflection and it’s very easy to use, so I recommend you take a look at it first.

This option and all subsequent ones convert the reflection code into a delegate that can be directly invoked without the overhead of reflection every time, hence the speed boost!

Although it’s worth pointing out that the creation of a delegate has a cost (see ‘Further Reading’ for more information). So in short, the speed boost is because we are doing the expensive work once (security checks, etc.) and storing a strongly typed delegate that we can use again and again with little overhead. You wouldn’t use these techniques if you were doing reflection once, but if you’re only doing it once it wouldn’t be a perforamnce bottleneck, so you wouldn’t care if it was slow!

The reason that reading a property via a delegate isn’t as fast as reading it directly is because the .NET JIT won’t inline a delegate method call like it will do with a Property access. So with a delegate we have to pay the cost of a method call, which direct access doesn’t.

Option 3 - Create a Delegate

In this option, we use the CreateDelegate function to turn our PropertyInfo into a regular delegate:

C#
// Setup code, done only once
PropertyInfo property = @class.GetProperty("Data", bindingFlags);
Func<TestClass, string> getDelegate = 
    (Func<TestClass, string>)Delegate.CreateDelegate(
             typeof(Func<TestClass, string>), 
             property.GetGetMethod(nonPublic: true));

[Benchmark]
public string GetViaDelegate()
{
    return getDelegate(testClass);
}

The drawback is that you to need to know the concrete type at compile-time, i.e., the Func<TestClass, string> part in the code above (no you can’t use Func<object, string>, if you do, it’ll thrown an exception!). In the majority of situations when you are doing reflection, you don’t have this luxury, otherwise you wouldn’t be using reflection in the first place, so it’s not a complete solution.

For a very interesting/mind-bending way to get round this, see the MagicMethodHelper code in the fantastic blog post from Jon Skeet ‘Making Reflection fly and exploring delegates’ or read on for Options 4 or 5 below.

Option 4 - Compiled Expression Trees

Here, we generate a delegate, but the difference is that we can pass in an object, so we get round the limitation of ‘Option 4’. We make use of the .NET Expression tree API that allows dynamic code generation:

C#
// Setup code, done only once
PropertyInfo property = @class.GetProperty("Data", bindingFlags);
ParameterExpression = Expression.Parameter(typeof(object), "instance");
UnaryExpression instanceCast = 
    !property.DeclaringType.IsValueType ? 
        Expression.TypeAs(instance, property.DeclaringType) : 
        Expression.Convert(instance, property.DeclaringType);
Func<object, object> GetDelegate = 
    Expression.Lambda<Func<object, object>>(
        Expression.TypeAs(
            Expression.Call(instanceCast, property.GetGetMethod(nonPublic: true)),
            typeof(object)), 
        instance)
    .Compile();

[Benchmark]
public string GetViaCompiledExpressionTrees()
{
    return (string)GetDelegate(testClass);
}

Full code for the Expression based approach is available in the blog post Faster Reflection using Expression Trees

Option 5 - Dynamic Code-gen with IL Emit

Finally, we come to the lowest-level approach, emiting raw IL, although ‘with great power, comes great responsibility’:

C#
// Setup code, done only once
PropertyInfo property = @class.GetProperty("Data", bindingFlags);
Sigil.Emit getterEmiter = Emit<Func<object, string>>
    .NewDynamicMethod("GetTestClassDataProperty")
    .LoadArgument(0)
    .CastClass(@class)
    .Call(property.GetGetMethod(nonPublic: true))
    .Return();
Func<object, string> getter = getterEmiter.CreateDelegate();

[Benchmark]
public string GetViaILEmit()
{
    return getter(testClass);
}

Using Expression tress (as shown in Option 4), doesn’t give you as much flexibility as emitting IL codes directly, although it does prevent you from emitting invalid code! Because of this, if you ever find yourself needing to emit IL, I really recommend using the excellent Sigil library, as it gives better error messages when you get things wrong!


Summary

The take-away is that if (and only if) you find yourself with a performance issue when using reflection, there are several different ways you can make it faster. These speed gains are achieved by getting a delegate that allows you to access the Property/Field/Method directly, without all the overhead of going via reflection every-time.


Further Reading

For reference, below is the call-stack or code-flow that the runtime goes through when Creating a Delegate.

  1. Delegate CreateDelegate(Type type, MethodInfo method)
  2. Delegate CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure)
  3. Delegate CreateDelegateInternal(RuntimeType rtType, RuntimeMethodInfo rtMethod, Object firstArgument, DelegateBindingFlags flags, ref StackCrawlMark stackMark)
  4. Delegate UnsafeCreateDelegate(RuntimeType rtType, RuntimeMethodInfo rtMethod, Object firstArgument, DelegateBindingFlags flags)
  5. bool BindToMethodInfo(Object target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags);
  6. FCIMPL5(FC_BOOL_RET, COMDelegate::BindToMethodInfo, Object* refThisUNSAFE, Object* targetUNSAFE, ReflectMethodObject *pMethodUNSAFE, ReflectClassBaseObject *pMethodTypeUNSAFE, int flags)
  7. COMDelegate::BindToMethod(DELEGATEREF *pRefThis, OBJECTREF *pRefFirstArg, MethodDesc *pTargetMethod, MethodTable *pExactMethodType, BOOL fIsOpenDelegate, BOOL fCheckSecurity)

The post Why is reflection slow? first appeared on my blog Performance is a Feature!

This article was originally posted at http://www.mattwarren.org/atom.xml

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGreat article Pin
#realJSOP6-Jan-17 6:29
professional#realJSOP6-Jan-17 6:29 
AnswerRe: Great article Pin
matt warren15-Jan-17 23:33
matt warren15-Jan-17 23:33 
GeneralMy vote of 5 Pin
Casey Shaar16-Dec-16 9:29
Casey Shaar16-Dec-16 9:29 
GeneralRe: My vote of 5 Pin
matt warren16-Dec-16 10:51
matt warren16-Dec-16 10:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.