Friday, November 6, 2009

Is this string numeric in C#?

On a completely unrelated project to my previous post about Optimizing a custom Trim() function in C# where I was stripping non-numeric characters from either end of a string that may have digits in it I found myself battling with a slow running console application that needed profiling.

Someone had recommended EQATEC Profiler to me and so I ran it against this console app to find where the slowness was coming from. It turned out that the bottleneck was in an IsNumeric function:

private readonly static Regex NumericRegex =
    new Regex(@"^[0-9]+$", RegexOptions.Compiled);
bool IsNumeric(string Text)
{
    return NumericRegex.IsMatch(Text);
}

When I discovered how frequently this was being called I went about optimizing it and came up with:

bool IsNumeric(string s)
{
    for (int i = 0; i < s.Length; i++)
    {
        if (!Char.IsDigit(s[i]))
        {
            return false;
        }
    }
    return true;
}

I then tested the two functions on two different strings:

string numericString = "12345678901234567890123456";
string nonNumericString = "abcdefghijklmnopqrstuvwxyz";

To see how they would fare against each other. On the numeric string the new function ran 3 times faster than the old regex one. On the non-numeric string it was a factor of nine. The reason for the huge difference in performance gain between the two types of strings should be fairly obvious. For the non-numeric string the new function bails out of the call immediately after inspection of the first character while the regex would still examine the whole string. For the numeric string both functions would need to examine every single character before returning.

More tests with 2 new algorithms from comment and email: Part 2

3 comments:

  1. What about an Int32.TryParse call? That's a native call that should be pretty well optimized and it returns a bool indicating whether parsing was successful. Depending on the size of your strings, you could also move up to Int64.TryParse.

    ReplyDelete
  2. Although unlikely it is possible that there might be a string of digits larger than 18,446,744,073,709,551,615 (Int64) so just for safety I'd have to exclude that.
    Although I am curious to know if that "algorithm" would be beat by TryParse(). Behind the scenes doesn't TryParse handle an exception and pass back false in the case of an exception and wouldn't that hit performance?

    ReplyDelete
  3. I've posted the results of trying Bill's idea on the next blog post along with another idea from Jon.

    ReplyDelete