Reflection Speed Test - How slow is it really?

Whenever anyone anywhere mentions Reflection on a blog post, straight away there is a reply saying "you shouldn't use Reflection as its slow". I thought I'd take a look at how "slow" reflection really is in the real world.

We use reflection in a couple of instances in our applications. One situation where we use Reflection is in a previous post about getting a friendly name from an enumeration. Another situation is where we convert a List of business objects into a DataTable (we need to do this produce PDF documents in our application using ASPOSE.Words - I'll post about this later). Another common situation you'll see is object hydration.

Getting a friendly name from an enumeration

There are a couple of other options I've seen/used in the past to get friendly names from Enums.

These consist of:

  1. using Reflection to get the value of an attribute on the enum (code from my previous post)
  2. storing the enum in a table in the database and looking it up at runtime
  3. storing the enum in some other way (e.g. XML) and looking it up at runtime
  4. retrieving the friendly name from a resources file at runtime
  5. using a switch/case statement to get the values of the enum

I'm going to test all of the above options and see how we go for speed.

The Results

I'm sure some of you won't want to read through how I did it, and what methods I used... so for those people here are the results.

Startup Time

Method Time Taken
(ticks*)
Time Taken
(ms)
Reflection 36,639 3.66
Database Warm-up 637,757 63.78
Database (Stored Procedure) 9,672 0.97
Switch / Case Statement 2,744 0.27
Resources 932,752 93.28
Xml Warm-up 14,712 1.47
Xml 8,225 0.82

* - A tick is 100 nanoseconds.

The above data represents a average of 5 runs, only getting the value of the enum once. This represents the cold-start time for each different method (basically I'm trying to exclude any form of caching). What we can see from the above results, is that by far, a SWITCH / CASE statement is the fastest method we have available. I've split out Database warm-up and database stored proc runtime into 2 separate runs (likewise with XML). I'm a database guy, and everyone knows that establishing a connection to the database is pretty costly. What was a surprise to me, is that the longest method was getting the value from a resources file.

But looking at the numbers above....and putting it into a bit of context, the longest method above took 93ms.... In terms of speed, this is still pretty fast, and certainly within tolerable levels for a Web UI.

100 Iterations

I've also run the results for 100 iterations. Why 100? Maybe I'm rendering a list of data, and this is one of the columns I want to display. I know 100 is a fairly big list to render, but lets just pretend....

Method Time Taken
(ticks*)
Time Taken
(ms)
Switch / Case Statement 74 0.0074
Resources 37,809 3.7809
Reflection with cache 67,015 6.7015

Again, we see that using a switch/case statement outperforms everything else MASSIVELY. I'm actually pretty surprised how fast it is. For 100 iterations of resources and reflection, we get response times of 3.7ms and 6.7ms respectively. Again, putting this in context, we're not talking about a process that's going to put the brakes on any website I develop.

What is interesting, is that using Resources is initially slow to startup, but then runs pretty past afterwards.

How I got the Values of the enum

I'll post the code I used to get the values of the enumerations.

EnumHelper - Reflection

This is the same method I used in a previous blog post. Its posted below as well in the Reflection with Cache code.

Database

This is pretty simple stuff. Basically open the connection to the database and run a stored procedure.

public static string GetDescription(Enum en)
       {
           using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SqlCommon"].ConnectionString))
           {
               SqlCommand cmd = new SqlCommand();

               conn.Open();
               cmd.Connection = conn;

               cmd.CommandType = CommandType.StoredProcedure;
               cmd.CommandText = "Enum_GetDescription";

               cmd.Parameters.Add("enumName", SqlDbType.VarChar).Value = en.ToString();

               SqlDataReader dr = cmd.ExecuteReader();

               if (dr.Read())
               {
                   return Convert.ToString(dr["Description"]);
               }
           }

           return en.ToString();
       }

Switch / Case statement

Basically pass in the enum, and return a string. The only drawback I can think of about this one, is that you'd need to write one of these for each enumeration you use in your application.

public static string GetDescription(UserColours en)
{
    switch (en)
    {
        case UserColours.AliceBlue: return "Alice Blue";
        case UserColours.BrightPink: return "Bright Pink";
        case UserColours.BurntOrange: return "Burnt Orange";
        case UserColours.DarkGreen: return "Dark Green";
        case UserColours.SkyBlue: return "Sky Blue";
        default: return en.ToString();
    }
}

Resources

Again, just read the value from a resources file, using the enumeration ToString() as the Name 

public static string GetDescription(Enum en)
{
    string s = Resources.UserColoursResource.ResourceManager.GetString(en.ToString());

    if (!string.IsNullOrEmpty(s))
        return s;

    return en.ToString();

}

image

Xml File

Load up the Xml file, and use XPath to find the friendly name of the enum

public static string GetDescription(Enum en)
{
    XmlDocument xmlDoc = new XmlDocument();

    xmlDoc.Load(HttpContext.Current.Server.MapPath("Enums.xml"));

    XmlNode node = xmlDoc.SelectSingleNode(String.Format("enums/enum[@value = \"{0}\"]", en.ToString()));

    if (node != null)
    {
        return node.Attributes["description"].Value;
    }

    return en.ToString();
}

 

<?xml version="1.0" encoding="utf-8" ?>
<enums>
    <enum value="BurntOrange" description="Burnt Orange" ></enum>
    <enum value="BrightPink" description="Bright Pink" ></enum>
    <enum value="DarkGreen" description="Dark Green" ></enum>
    <enum value="SkyBlue" description="Sky Blue" ></enum>
    <enum value="AliceBlue" description="Alice Blue" ></enum>
</enums>

Resources with caching

This is effectively the same as my standard EnumHelper class library, except I've added a caching using the HttpContext Cache.

public static class RelfectionEnumHelper
{
    private static object padLock = new object();

    public static string GetDescription(Enum en)
    {
        Type type = en.GetType();

        MemberInfo[] memInfo = type.GetMember(en.ToString());

        if (memInfo != null && memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0)
            {
                return ((DescriptionAttribute)attrs[0]).Description;
            }
        }

        return en.ToString();
    }


    public static string GetDescriptionUsingCache(Enum en)
    {
        System.Web.Caching.Cache cache = System.Web.HttpContext.Current.Cache;

        if (cache[en.ToString()] == null)
        {
            lock (padLock)
            {
                if (cache[en.ToString()] == null)
                {
                    cache.Insert(en.ToString(), GetDescription(en));
                }
            }
        }

        return cache[en.ToString()].ToString();
    }
}

 

Conclusion

Personally, I'm going to stick with using my EnumHelper library with caching. I'm really not that fussed about shaving 3ms of a page hit...

The reasons I'll stick with this library are:

  • its nice and simple. The enum and its friendly name are located in the one spot. Developers are lazy.... if they have to maintain things in 2 or 3 different locations... plain and simple they won't!
  • its fast enough for me. None of my clients ever ask us to make it go faster. (There are usually other way we can make things run faster anyway)

12 Comments

  • you shouldn't use Reflection as its slow.

    [Grin]

  • ha ha... Nice work Dave... should've seen that one coming.. didn't. Well done

  • A static dictionary might give you much better results than using the cache.

    Given that there isn't much cost to the lookup. you could probably do away with the lock too.

  • The XML would probably also improve if you used some simple caching. A generic dictionary with key and value string.
    Using dictionary.TryGetValue will also save the lookup twice.

  • @AndrewSeven: agree
    a static dictionary instead of static padLock would be mij choice of cache but... I didn't test it.

  • If you make the GetDescriptionUsingCache(Enum en) and extension method

    public static string GetDescriptionUsingCache(this Enum en)

    you can use the following syntax which IMHO is nicer.
    In addition to:
    RelfectionEnumHelper.GetDescriptionUsingCache(MyOtherEnum.Test1);

    You can call it off the enum.
    MyOtherEnum.Test1.GetDescriptionUsingCache();

  • Well, there is a thing that has not been taken into consideration : What about localization ? In many applications, display-names of enumerated values should be localized. As far as I can see, only the ressource file, the xml based and DataBase based solutions are localizable. Any idea about it?

  • How will th Description attribute approach deal with multiple languages?

  • @AndrewSeven - The description attribute doesn't handle multiple languages, it only copes with one language. TO do this with multiple languages you'd probably have to go down one of the paths I've described above... store the description in XML, DB or RESX.

  • Yes! reflection is relatively slow but how guys come on how a couple of miliseconds will give you performance boost on avg. think about it Reflection saves you from lots of coding cost trouble and provides you a much generic library... and the performance hit not more than some miliseconds....
    why are you worried even as now computing power is increasing rapidly now we are talking about cores and gb of bus speed... in this this hit is nothing.
    and still you can gain that loss from some other techniques....

  • Use serialized Binary data then xml data at all, and you get better speed or use line by line in file reading it was lot of time faster then xpath at all.

  • Good to start

Comments have been disabled for this content.