Which string to int conversion method is the fastest
conversion speed experiment C#Using conversion methods in any language is a fundamental part of every task, even small ones. If we have to use them often, in some cases performance is a key point (it’s always important, but there are more important cases). So the question is, which conversion method is the fastest?
Conversion basics
Conversion is a fundamental part of programming, mainly in typed languages like C#. When you have variables with dedicated types, it means that for a given type you can only use given values. From the examples below you can see that a long
type’s value can’t fit into an int
type – if you take eg. the highest value of long.
Examples:
- int
- signed 32-bit integer
- range: -2,147,483,648 to 2,147,483,647
- long
- signed 64-bit integer
- range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
Sometimes we need to switch between type A and type B; but instead of switching we call this as conversion, because we want to convert type A to type B.
Why do we need this? Well, with a simple example I can demonstrate it. Imagine that you have a text file with person names and birthyears. Out of this, you want to create person objects in your code. In this object, you want to have the name as a string type, and a birthyear as an integer type. Types are important, because if/when you want to calculate how old is a given person, you can easily determine it based on the current year minus the birthyear of the person. But here comes the problem: what happens when you do not have birthyear as an integer, but as a string type? On strings, you can’t apply subtraction.
So to handle these cases, we have conversion methods for most types. In this example I will check string to int conversions.
Speed comparison
Since conversion counts as a fundamental part of coding, we must make sure that it is fast as hell, especially when we need to use it frequently.
In this example I will check for three different string to int conversions, which are the most often used variants (there are other versions):
Convert.ToInt32("300");
int.Parse("300");
Int32.Parse("300");
Creating testing environment
For testing, I will have these three conversions inside three different methods, which will be called later on. Each call’s time will be measured programatically.
For measuring I will use the StopWatch
class, which is part of the System.Diagnostics
namespace.
Conversion methods
I will run each conversion 10,000 times in order to have a bigger timespan, rather than just calling once each method.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void ConvertToInt32Test()
{
for (int i = 0; i < 10000; i++)
Convert.ToInt32("300");
}
static void IntParseTest()
{
for (int i = 0; i < 10000; i++)
int.Parse("300");
}
static void Int32ParseTest()
{
for (int i = 0; i < 10000; i++)
Int32.Parse("300");
}
TestRun class
1
2
3
4
5
6
7
8
9
10
class TestRun
{
public MemberInfo MethodName { get; set; }
public TimeSpan TimeSpan { get; set; }
public override string ToString()
{
return $"{TimeSpan} - {MethodName.Name}";
}
}
Main part
Firstly, I will create an Action
delegate, which will hold the three methods. After I will create a StopWatch
object and a List
where the TestRun
objects will be stored.
1
2
3
Action[] methods = new Action[] { ConvertToInt32Test, IntParseTest, Int32ParseTest };
Stopwatch sw = new Stopwatch();
List<TestRun> results = new List<TestRun>();
After this I will create a for loop, which will iterate numberOfTestRuns
times (this could be any number like 10, 100, 200). Inside this there will be an other for loop, which will iterate through the methods. Inside the stopwatch will be (re)started, invoking the methods and then stopping the stopwatch. After stopping the method name and elapsed time will be added to the list. Note that every conversion method has exactly 10,000 calls inside.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int numberOfTestRuns = 10;
for (int i = 0; i < numberOfTestRuns; i++)
{
for (int j = 0; j < methods.Length; j++)
{
sw.Restart();
methods[j]();
sw.Stop();
results.Add(new TestRun()
{
MethodName = methods[j].Method,
TimeSpan = sw.Elapsed
});
}
}
Now, after this I will have the list will all the various result outputs. The next step is to calculate the avarage value of each conversion type. For this I will use LINQ group by query.
I write all the recorded test runs to a file to see everything if needed.
After the query, many things could be done, for this time I will write out the output simply to the console, after ordering it by the calculated average values.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var q = from x in results
group x by x.MethodName.Name into g
select new
{
METHOD = g.Key,
AVG_SCORE = Math.Round(g.Average(a => a.TimeSpan.TotalMilliseconds),6)
};
StreamWriter swr = new StreamWriter("_OUTPUT.txt");
results.ForEach(x => swr.WriteLine(x));
swr.Close();
q.OrderByDescending(x => x.AVG_SCORE).ToList().ForEach(x => {
Console.WriteLine($"{x.AVG_SCORE} ms : {x.METHOD}");
});
Results
Based on “one test is no test”, the full logic is implemented according to this. The outer for loop counts n times, and then the average is calculated of 10, 100, 200 test runs. So the output can be taken as an average value, not only one test’s output. (This is important.)
Output for 10 runs
- 1,10565 ms : ConvertToInt32Test
- 1,07781 ms : Int32ParseTest
- 0,99899 ms : IntParseTest
Output for 100 runs
- 1,001716 ms : ConvertToInt32Test
- 0,973254 ms : IntParseTest
- 0,971422 ms : Int32ParseTest
Output for 1000 runs
- 0,971969 ms : ConvertToInt32Test
- 0,944418 ms : IntParseTest
- 0,943066 ms : Int32ParseTest
Results plotted
Conclusion
Based on the results, I can say that the number of the test runs does not influence the output. In all cases (10, 100, 1000) the order remained the same, which means that the speed were the same in all dimensions. (If we think about it, it makes sense. The reason for this is simple: the conversion itself was the same in all cases, converting “300” to 300. Also the fact that it is called 10,000 times or just once (comment out the for loop) does not make a difference.)
The second thing that can be seen is what this full article about, which is the fastest? The winner is: Int32.Parse()
.
Sidenote
I personally mainly use int.Parse()
, which is the second best in the line, it can be accepted as good. Also worth mentioning, these numbers are so tiny, that in most cases it does not affect our work; but when it does, it’s good to know that we can measure their speed.
Opportunity for improvement
- Adding more test methods: Since the action delegate can be extended easily, additional methods can be added to the testing. Nothing else has to be changed, everything after that works automatically.
- Experimenting with parameters: Modifying the parameters (test run number, for loop number etc.) different scenarios can be created, which may or may not create different outputs.
- Experimenting with conversion values: Creating different conversions can also make some difference. Instead of converting always “300” to 300, experiment with random data which changes in each method call.
- Filtering data: Removing peaks from the results can also make some difference.