What is the fastest way to iterate a dynamic object in C#?

10,737
            try
            {
                if(admObj.Key == "Title")
                {
                    encodedValue = admObj.Value.ToString();
                }else
                {
                    encodedValue = admObj.Value[0].ToString();
                }
            }
            catch (Exception)
            {
                encodedValue = admObj.Value.ToString();                   
            }

I don't have a C# compiler in front of me, or access to your data, but this looks suspect to me. Exceptions are extremely slow - are you hitting the catch block often? If you are, try to work out what's causing the exceptions and handle them without causing an exception - leave exceptions to handle rare situations you haven't thought of.

Edit: Final solution is in the question edit - I won't repeat it here. If you have a performance problem and you have exception-handling somewhere in your loop, that's often the very first thing to try and eliminate. Exceptions are awfully slow - much more so than you might think. They're best kept for very unusual circumstances.

Share:
10,737
Matt Cashatt
Author by

Matt Cashatt

I love to code and really enjoy being on Stackoverflow. It has taught me a ton.

Updated on June 24, 2022

Comments

  • Matt Cashatt
    Matt Cashatt about 2 years

    Background

    I am working on an application that requires the consumption of JSON services on the server, and then repackaging of that JSON into a view model that can be referenced using Razor syntax in the view. Server-side manipulation of this code is a must for various reasons.

    We are using C#, .NET 4, MVC3, Razor, JsonFx.

    We currently have code that works just fine, but it is taking up to a minute to iterate 250 items in the received JSON object and this is unacceptable (not to mention baffling). I have already isolated the problem to the following loop; the JSON comes in lightening-fast so that is not the problem. Here is the working but extremely slow code:

            var reader = new JsonReader();
            var json = GetJson(SPListName);
    
            var admItems = new List<IDictionary<String, object>>();
            dynamic _items = reader.Read(json); //This part is REALLY fast.  No problem here.
            foreach (var itm in _items)
            {
                dynamic obj = new ExpandoObject();
                foreach (dynamic admObj in itm)//Here begins the slow part.
                {
                    var item = obj as IDictionary<String, object>;
                    var encodedValue = "";
                    try
                    {
                        if(admObj.Key == "Title")
                        {
                            encodedValue = admObj.Value.ToString();
                        }else
                        {
                            encodedValue = admObj.Value[0].ToString();
                        }
                    }
                    catch (Exception)
                    {
                        encodedValue = admObj.Value.ToString();                   
                    }
    
                    item[admObj.Key] = encodedValue.EncodeNonAscii().FixHtmlEntities();
                }
                admItems.Add(obj);
            }
            return admItems;
    

    You may also notice a few custom extension methods. Here they are (in case that matters):

    public static string EncodeNonAscii(this Object str)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (char c in str.ToString())
                    {
                        if (c > 127)
                        {
                            // This character is too big for ASCII
                            string encodedValue = "\\u" + ((int) c).ToString("x4");
                            sb.Append(encodedValue);
                        }
                        else
                        {
                            sb.Append(c);
                        }
                    }
                    return sb.ToString();
                }
    
                public static string FixHtmlEntities(this Object str)
                {
                    var fixedString = str.ToString().Replace("\\u00c2\\u00ae", "&reg;");
                    return fixedString;
                }
    

    Question

    What the heck am I doing wrong/how do I speed this up. My brain is hamburger right now so I hope someone points out a simple oversight.

    Update/Resolution

    Rophuine and Joshua Enfield both pointed to the root of the speed issue: the catching of exceptions was slowing everything down.

    Many folks suggested that I use Json.Net or something similar. While I appreciate that advice, it really wasn't at the root of my problem (even though it may have appeared that way); I have used Json.Net extensively in the past and came to prefer JsonFx over it a few months ago. In this particular case, I am more concerned with the construction of a view model object as I have already deserialized the JSON using JsonFx. Please let me know if you still think I am missing your point ;).

    The reason for my brain-dead try/catch scheme was that I needed to do different things with each property of the JSON string depending on it's type (string, string[], object[], etc). I forgot that System.Type can handle that for me. So here is the final code:

    var reader = new JsonReader();
                    var json = GetJson(SPListName);
    
                    var admItems = new List<IDictionary<String, object>>();
                    dynamic _items = reader.Read(json);
                    foreach (var itm in _items)
                    {
                        dynamic obj = new ExpandoObject();
                        foreach (dynamic admObj in itm)
                        {
                            var item = obj as IDictionary<String, object>;
                            var encodedValue = "";
    
                            Type typeName = admObj.Value.GetType();
    
                            switch (typeName.ToString())
                            {
                                case("System.String[]"):
                                    encodedValue = admObj.Value[0].ToString();
                                    break;
                                default:
                                    encodedValue = admObj.Value.ToString();
                                    break;
                            }
    
                            item[admObj.Key] = encodedValue.EncodeNonAscii().FixHtmlEntities();
                        }
                        admItems.Add(obj);
                    }
                    return admItems;
    

    Hope this helps someone!

    • Joshua Enfield
      Joshua Enfield over 12 years
      How many exceptions are occuring? If it's border-line flow control exception handling may be slowing you down some, but a minute sounds awfully long for 250 items. Do you really need to use dynamic here?
    • Khairuddin Ni'am
      Khairuddin Ni'am over 12 years
      Are there any reason you don't use json library such as JSON.NET or ServiceStack.NET? It's easier (and also fast) to parse json string.
    • L.B
      L.B over 12 years
      In addition to @Ni'am, you can have a dictionary of objects with a single line of code JsonConvert.DeserializeObject. Examples: stackoverflow.com/questions/8662233/parsing-non-standard-jso‌​n/… stackoverflow.com/questions/8887029/parsing-json-file-c-shar‌​p/…
    • Matt Cashatt
      Matt Cashatt over 12 years
      I think that @JoshuaEnfield and Rophuine are on the money. Unfortunately the server that issues the JSON just went down. Ugh! Will let you guys know shortly. Thanks!
    • L.B
      L.B over 12 years
      @MatthewPatrickCashatt, I also think that your problem is related with the exceptions, but using for ex, Json.Net can help you to replace all of your code you posted with a single line( and no exceptions any more)
    • Daniel Mann
      Daniel Mann over 12 years
      Did you profile your application using a tool like ANTS or the built-in VS profiler?
    • Matt Cashatt
      Matt Cashatt over 12 years
      @DBM--No I did not. In fact, I have never done that and am very interested in learning how. So are these good tools to begin learning about profiling?
  • Matt Cashatt
    Matt Cashatt over 12 years
    Cheers Rophuine! Thanks for the help. Please see my final update above and feel free to incorporate any of it into your answer as you see fit.
  • Stephen Oberauer
    Stephen Oberauer almost 11 years
    Apparently exceptions are only slow in debug mode, however as implied by the name, exceptions should be the exception and not the rule. yoda.arachsys.com/csharp/exceptions.html