Compartilhar via


Performance Profiling Parse vs. TryParse vs. ConvertTo

When programming in .Net you are presented with several different ways to extract a numerical value (I’m using an Int32 for my example) from a string. Recently I was looking at the differences between Parse, TryParse and ConvertTo. So I figured that I would use the new performance profiler included with Visual Studio Team System 2005 to figure out the performance differences between these three functions.

 

            The first of these functions, Parse, is one that should be familiar to any .Net developer. This function will take a string and attempt to extract an integer out of it and then return the integer. If it runs into something that it can’t parse then it throws a FormatException or if the number is too large an OverflowException. Also, it can throw an ArgumentException if you pass it a null value.

 

            TryParse is a new addition to the new .Net 2.0 framework that addresses some issues with the original Parse function. The main difference is that exception handling is very slow, so if TryParse is unable to parse the string it does not throw an exception like Parse does. Instead, it returns a Boolean indicating if it was able to successfully parse a number. So you have to pass into TryParse both the string to be parsed and an Int32 out parameter to fill in. We will use the profiler to examine the speed difference between TryParse and Parse in both cases where the string can be correctly parsed and in cases where the string cannot be correctly parsed.

 

            The Convert class contains a series of functions to convert one base class into another. I believe that Convert.ToInt32(string) just checks for a null string (if the string is null it returns zero unlike the Parse) then just calls Int32.Parse(string). I’ll use the profiler to confirm this and to see if using Convert as opposed to Parse has any real effect on performance.

 

            For a CPU bound application like this I’ll be using the sampling mode of the profiler. Sampling mode takes a snapshot of program state in some periodic cycle (I’ll be using clock cycles as my metric) and has a much lower overhead then instrumentation mode for a test like this. The three main test functions that I will be using are as follows:

 

private List<Int32> TestParse(String[] strings)

{

Int32 intValue;

List<Int32> intList = new List<int>();

for (int i = 0; i < 5000000; i++)

{

intList.Clear();

foreach (String str in strings)

{

try

{

intValue = Int32.Parse(str);

intList.Add(intValue);

}

catch (System.ArgumentException)

           { }

              catch (System.FormatException)

   { }

     catch (System.OverflowException)

             { }

}

}

return intList;

}

private List<Int32> TestTryParse(String[] strings)

{

Int32 intValue;

List<Int32> intList = new List<int>();

Boolean ret;

for (int i = 0; i < 5000000; i++)

{

intList.Clear();

foreach (String str in strings)

              {

              ret = Int32.TryParse(str, out intValue);

          if (ret)

                   {

                     intList.Add(intValue);

                   }

             }

   }

     return intList;

}

private List<Int32> TestConvert(String[] strings)

{

Int32 intValue;

List<Int32> intList = new List<int>();

for (int i = 0; i < 5000000; i++)

{

intList.Clear();

foreach (String str in strings)

{

try

{

intValue = Convert.ToInt32(str);

intList.Add(intValue);

}

catch (System.FormatException)

{ }

catch (System.OverflowException)

{ }

}

}

return intList;

}

            To test the performance when good strings are passed in I used the following set of strings: { "123", "4567", "7890", "1", "1231280", "10" }. Show below is calltree view with the Main program function set as the root and with just the nodes below it expanded. Looking at this view we can see that about equal time was spent in all three of the conversion functions.

 

 

            On the next screen, I’ve expanded some of the callstacks below the TestConvert, TestParse and TestTryParse functions. As you might have guessed, the callstacks down here are very similar, especially for TestConvert and TestParse. The only real difference between those two is that TestConvert is making a call toThread.get_CurrentCulture to check for a null string before it calls down to Number.ParseInt32. TestTryParse has the same basic structure as TestParse, except it calls Number.TryParseInt32 instead of Number.ParseInt32. But all three functions end up at the same System.Number.ParseNumber where they all spend about the same amount of time (Convert spends a little less but I do not think this is enough to be statistically significant).

 

 

            So if all of these functions take about the same time with good data, how about if we make them parse bad data? The second set of data that I will use will have some oddballs like invalid characters, nulls, overflows and empty strings along with a couple of valid strings to parse. Here is the string set: { "12345", null, "123", "1324dfs", "51235", String.Empty, "43", "4123412341234123412341234123412341234123" }. I did have to reduce the number of iterations in each loop down from 5000000 to 50000 (that’s two zeros removed if you don’t want to count) in order to get approximately the same number of samples. Shown below you can see the huge difference between TryParse, Parse and ConvertTo when you are using bad data.

 

 

            So while TryParse is only taking .5% of execution time Parse is taking 18% while Convert is taking 14%. The difference is, as we guessed, in the exception handling code. That is why Convert was faster then Parse as Convert handles the null string in the bad data set without throwing an exception. Shown below you can see where all the execution time is going to inside of the TestParse function. Most of the time is spent in the constructor for ArgumentNullException (in red) and in looking up the resource string for the exception (in blue).

            So hopefully this little walkthrough has convinced you that if you plan on parsing a large number of strings you should use the new TryParse function instead of the old Parse or ConvertTo functions. Exceptions are meant to be “exceptions to the rule” not something that you want to be happening often as it can kill your program’s performance. So if you expect a lot of strings that you cannot parse make sure to use TryParse.

Comments

  • Anonymous
    December 19, 2005
    Well a problem with tryparse is that it is sooo tedious to use - yolu have to do this:

    ret = Int32.TryParse(str, out intValue);

    if (ret)

    {

    intList.Add(intValue);

    }

    instead of this:

    intValue = Int32.Parse(str);

    intList.Add(intValue)

  • Anonymous
    December 20, 2005
    You're kind of comparing apples to oranges there. You're doing error checking for TryParse but not for Parse.

    In reality it would be

    Try
    intValue = Int32.Parse(str)
    intList.Add(intValue)
    Catch Ex As FormatException
    ' Error Handling
    End Try

    vs

    If Int32.TryParse(str, intValue) Then
    intList.Add(intValue)
    Else
    ' Error Handling
    End If

  • Anonymous
    December 22, 2005
    deepIce2, well you didn't include a try/catch around your Int32.Parse example - if you had, TryParse would be less lines of code ;) And also, instead of :

    ret = Int32.TryParse(str, out intValue);
    if (ret)
    {
    intList.Add(intValue);
    }


    why not just do

    if(Int32.TryParse(str, out intValue);
    {
    intList.Add(intValue);
    }

    Personally I love TryParse - it's more efficient and faster to work with :)

  • Anonymous
    December 22, 2005
    A couple of folks have accused me of comparing apples to oranges, and I must admit that they have me nailed. I was comparing apples to oranges, but I was also doing it on purpose ;-). I was just showing how much time you do save by doing your own basic error checking with TryParse as opposed to just catching the exceptions thrown by Parse.

  • Anonymous
    March 06, 2006
    The comment has been removed

  • Anonymous
    June 09, 2006
    En .Net 1.1, vous vous &#234;tes peut &#234;tre d&#233;j&#224; servi de la m&#233;thode TryParse du type Double.En .Net 2.0,&amp;nbsp;plusieurs...

  • Anonymous
    July 26, 2007
    PingBack from http://blog.zoomasp.net/?p=18

  • Anonymous
    October 02, 2007
    TryParse method is exposed by several classes in the System namespace System.Boolean.TryParse(System

  • Anonymous
    January 06, 2008
    PingBack from http://data-division.global-destruction.de/?p=112

  • Anonymous
    February 21, 2008
    PingBack from http://gadingretak.com/blog/?p=13

  • Anonymous
    November 30, 2008
    PingBack from http://sailjamehra.wordpress.com/2008/11/30/realtime-project-coding-standards/

  • Anonymous
    December 06, 2008
    PingBack from http://sailjamehra.wordpress.com/2008/12/06/real-time-project-coding-standards/

  • Anonymous
    December 09, 2008
    PingBack from http://thumatiravivarma.wordpress.com/2008/12/10/coding-standards-and-best-practices/

  • Anonymous
    January 23, 2009
    PingBack from http://rthumati.wordpress.com/2009/01/23/best-practices-for-c/