10

I've got a program written in c# where there are a lot of comparisons between ints and strings.

So for performance reasons, I would just like to know which is more efficient?

If we have:

int a  = 5;
string b = "5";

if(a == int.Parse(b)) { }

OR

if(a.ToString() == b) { }
Vikrant
  • 4,920
  • 17
  • 48
  • 72
The_Butcher
  • 2,440
  • 2
  • 27
  • 38
  • 4
    Stock answer for these questions: Have you tried actually profiling it for your data? – Dominic Rodger Oct 29 '09 at 10:07
  • Im a noob :/ How would i go about profiling this? – The_Butcher Oct 29 '09 at 10:10
  • Execute both variants in a loop of a few thousand or million iterations and measure the time it takes. – Joey Oct 29 '09 at 10:11
  • You use a profiling tool to measure the time for each method. – Dani Oct 29 '09 at 10:11
  • It is most efficient to compare int to int. The real question however is how expensive are `int.ToString` and `int.Parse`. `int.Parse` is probably much more expensive, but can't confirm that at the moment. – Kobi Oct 29 '09 at 10:13
  • 1
    Measure time with the debug stopwatch in c#? – The_Butcher Oct 29 '09 at 10:13
  • Before wasting time by badly profiling with loops, you should ask your self : Is it a real bottleneck ? – Guillaume Oct 29 '09 at 10:13
  • @Guillaume well there are potentially hundrends - thousands of comparisons per Request, so im inclined to say yes – The_Butcher Oct 29 '09 at 10:15
  • Kobi > ToString can also be expensive, a string is allocated on the heap. Building a string can be more expensive than reading one... – Guillaume Oct 29 '09 at 10:16
  • @The_Butcher You are inclined, to know, you should try with a real profiler. You can easily find one by searching on SO or Google. – Guillaume Oct 29 '09 at 10:17
  • Aqtime is a good profiler, and you can download a trial I think. you must check though that you will not sabotage your own test - the compiler is pretty smart and can use techniques to shorten the process time if you do the same exact function again and again and again... – Dani Oct 29 '09 at 10:20
  • @Butcher use the code listing A I have provided against the one you have mentioned in the question and the selected answer for better results. – Binoj Antony Oct 29 '09 at 11:06

11 Answers11

10

I actually profiled this using a few examples and timed loops. It turns out Parse wins for small integers, and ToString wins for large ones. This difference is so small it should not be a concern however, as others have mentioned, you're likely to make a better choice by thinking about the cases where the string does not represent an integer at all.

Edit: For those interested, here's the source, quick 'n' dirty:

using System;
using System.Diagnostics;

namespace CompareTest
{
    static class Program
    {
        static void Main(string[] args)
        {
            int iterations = 10000000;
            int a = 5;
            string b = "5";

            Stopwatch toStringStopwatch = new Stopwatch();
            toStringStopwatch.Start();

            for (int i = 0; i < iterations; i++) {
                bool dummyState = a.ToString() == b;
            }

            toStringStopwatch.Stop();

            Stopwatch parseStopwatch = new Stopwatch();
            parseStopwatch.Start();

            for (int i = 0; i < iterations; i++) {
                bool dummyState = a == int.Parse(b);
            }

            parseStopwatch.Stop();

            Console.WriteLine("ToString(): {0}", toStringStopwatch.Elapsed);
            Console.WriteLine("Parse(): {0}", parseStopwatch.Elapsed);
            Console.ReadLine();
        }
    }
}
SoftMemes
  • 5,602
  • 4
  • 32
  • 61
7

A few comments mentioned running a profiling tool to prove which has better performance.

This is a ok, but the simplest way to check performance of specific statements is to put them in a loop and use the Stopwatch class.

Jeff Atwood asked about making this sort of timing even simpler in this question. In that question and answer you will also find some good code examples and background details.

Heres a very simple working example:

    System.Diagnostics.Stopwatch sw=new System.Diagnostics.Stopwatch();


    int a  = 5;
    string b = "5";

    sw.Start();

    for (int i=0;i<1000000;i++)
    {
        if(a == int.Parse(b))
        {

        } 
    }

    sw.Stop();

    Console.WriteLine("a == int.Parse(b) milliseconds: " + sw.ElapsedMilliseconds);

    sw.Reset();

    sw.Start();

    for (int i=0;i<1000000;i++)
    {
        if(a.ToString() == b)
        {

        }       
    }       

    sw.Stop();

    Console.WriteLine("a.ToString() == b milliseconds: " + sw.ElapsedMilliseconds);

On my computer it outputs:

a == int.Parse(b) milliseconds: 521

a.ToString() == b milliseconds: 697

So in this simple scenario int.Parse() is slightly faster, but not enough to really worry about.

Community
  • 1
  • 1
Ash
  • 60,973
  • 31
  • 151
  • 169
  • is that the Debug Stopwatch Class? soz im a bit of a newbie when it comes to c# :) – The_Butcher Oct 29 '09 at 10:24
  • 1
    It's the System.Diagnostics.Stopwatch class. You should be able to just type: Stopwatch sw=new Stopwatch(); and begin usng it (it's in System.dll). – Ash Oct 29 '09 at 10:26
  • 2
    1000000 runs => difference less than 200ms. This may not be a case for an optimisation. If the queries are slow, use a profiler. If they are fast enough, why would you waste time optimising ? – Guillaume Oct 29 '09 at 10:35
  • Any reason you're starting the stopwatch twice for the first round, including the variable assignment? Shouldn't be large enough to skew the results, but still... – SoftMemes Oct 29 '09 at 10:36
  • @Freed, because I was typing faster then my brain could follow ;) nice pick-up! I've removed the first Start(). – Ash Oct 29 '09 at 10:54
  • Can you try the string as a hexadecimal? I'm surprised (I'm not doubting the result though) that it's faster considering all the logic int.Parse has to perform – Chris S Oct 29 '09 at 10:57
  • @Chris, isn't the logic that ToString() has to perform very much alike what Parse() needs to do..? – SoftMemes Oct 29 '09 at 11:00
  • If you try ToString("D") it may be quicker. This uses the Int32ToDecStr function in the CLR. No format (just ToString()) is equivalent to "G" which uses NumberToString() which parses a format. So @freed yes I'm wrong above – Chris S Oct 29 '09 at 12:11
  • ToString("D") is no quicker at all. string.Format("{0:D}",a) is a lot slower – Chris S Oct 29 '09 at 12:23
4

Your choice is between the following
Code Listing A

int a = 5;
string b = "5";
//Assuming these two values are input received by the application at runtime
int bInt;
if (int.TryParse(b, NumberStyles.None, CultureInfo.InvariantCulture, out bInt) 
    && a.Equals(bInt))
{

}

and

Code Listing B

int a = 5;
string b = "5"; 
//Assuming these two values are input received by the application at runtime
if (string.Compare(b, a.ToString(), StringComparison.Ordinal) != -1)
{

}

I have tested this with stopwatch (as given in the selected answer) and found Code Listing A to be much faster. But Code Listing B is more readable!

Code Listing A beats if(a == int.Parse(b))

Binoj Antony
  • 15,886
  • 25
  • 88
  • 96
3

Internally, ToString and Parse do the following:

Parse

value = 0
for each char in string
  value = value * 10 + valueof(char) // i.e. '0' -> 0, '7' -> 7

ToString

string=""
while value > 0
  string.insert_at_front value % 10 // so that 0 -> '0' and 6 -> '6'
  value /= 10

// on IA32, the % and / can be done at the same time but requires
// a 64bit source for 32bit values

The ToString should be slower than Parse since division is generally slower than multiplication. However, the above doesn't take into account any overhead the Parse and ToString functions might perform during the conversion (i.e. generating exceptions, allocating memory), which means it's not as clear-cut which will more optimal.

From the other answers it seems the difference is marginal anyway so just use whatever make more sense to you.

Skizz
  • 69,698
  • 10
  • 71
  • 108
2

You've already gotten a few good responses, but let me add a couple of small points.

  1. One of the well-known risks with micro-benchmarks is that a small number of repetitions can end up measuring noise (e.g. the timings can be skewed by an incoming email or IM), but a large number of repetitions can end up measuring the performance of your garbage collector (e.g. if your code is constantly creating and discarding strings).

  2. When I find myself in an awkward spot in code, it's sometimes helpful to ask myself, "What assumptions or choices put me in this situation? What could I do differently?" For example (just guessing), when you wrote "...where there are a lot of comparisons between int's*[sic]* and strings", does that imply that you may be using the same values repeatedly (e.g., comparing new values against previous values)? If so, could you convert each string to int, and cache the converted value for subsequent re-use, instead of having to convert it again later?

joel.neely
  • 30,725
  • 9
  • 56
  • 64
2

My case that brought me here was to check if "5" == 5 in a switch-case and because I will always receive numbers between 0 and 9 I found out that the fastest way is: (int)b[0] == 53 So I'm taking the fist character of string "5" (b[0]) and cast it to it ACSII value which is 53 and after that compare. Here are the results: a == int.Parse(b) milliseconds: 194 a.ToString() == b milliseconds: 142 a == (int)(b[0]) milliseconds: 8 Even though it is very unusual case the difference on a massive array is obvious;

EDIT: As Dirk Horsten requested. Wrong as I am. I mentioned in my post that I use that on a switch case so I will use in all of my cases the ASCII values so it will look like that: switch((int)b[0]) { case 48: Console.WriteLine("0"); break; case 49: Console.WriteLine("1"); break; case 50: Console.WriteLine("2"); break; case 51: Console.WriteLine("3"); break; case 52: Console.WriteLine("4"); break; case 53: Console.WriteLine("5"); break; case 54: Console.WriteLine("6"); break; case 55: Console.WriteLine("7"); break; case 56: Console.WriteLine("8"); break; case 57: Console.WriteLine("9"); break; } And for a good order sake here are the results as you asked me: a == int.Parse(b) milliseconds: 184 a.ToString() == b milliseconds: 135 a + 48 ==(int)b[0] milliseconds: 8 As you could see there is no such a big difference just adding one addition.

  • your answer is brilliant :) but wrong :( The correct thing to do is `a + 48 == (int)(b[0])`, because `(int)("0"[0]) == 48`. Please run your benchmark with this code and tell us the results. – Dirk Horsten Dec 16 '15 at 08:57
1

The larger the number I'll go for the first method. a. if b is not a number, it will failed before trying to compare. b. string are compared by the length and number at once.

Dani
  • 14,639
  • 11
  • 62
  • 110
1

I doubt either call will really significantly impact your application unless you really are creating something on a grand scale.

Both techniques are creating a new string, however int.ToString() has to perform a lot less tasks than int.Parse().

int.ToString() is performed internally in the CLR (comnumber). int.Parse() is done inside the BCL source using Number.ParseInt32() -> Number.StringToNumber() -> Number.ParseNumber().

ParseNumber performs a huge number of checks so from a finger in the air guess you would imagine int.ToString() is faster. As others have mentioned, a proper performance test with the StopWatch class will be a better way to find out. You will want to try this out with the number format you are expecting: decimal, hex.

You can compare the C++ that the CLR is using for ToString() here: Look for

  • NumberToString (that's used for ToString() and other formats) which is used in FCIMPL3, called by int.ToString() as an extern call.
  • Int32ToDecStr is used for the "D" formatter.

The C#

var x = 5.ToString("D");
var y = 5.ToString();

I could be wrong about FCIMPL3, please correct me if I am.

Chris S
  • 64,770
  • 52
  • 221
  • 239
0

Parsing a String to an Int32 requires more performance and is more sensitive to errors. You will have to make sure the Int32.Parse will be successful in the first place. Also you can use an alternative for '=='. Use .Equals(), this easier to read and understand.

if(b.Equals(a))
{

}
D. Veloper
  • 1,497
  • 5
  • 16
  • 24
0

Also i read somwhere (MSDN) using the following is faster than == for string comparisons

StringA.ToUpperInvariant() == StringB.ToUpperInvariant()
  • What is the benefit of using upper case when comparing numbers? – Kobi Oct 29 '09 at 10:25
  • 1
    Moreover, it's faster to use an Equals with OrdinalIgnoreCase (or InvariantCultureIgnoreCase) than allocating new uppercase strings. – Guillaume Oct 29 '09 at 10:37
-1

There are many ways of representing the same number as a string...

ima
  • 8,105
  • 3
  • 20
  • 19