When doing a simple performance measurement, I was astonished to see that calling String.IndexOf(char)
was actually slower than doing it manually! Is this really true?! Here is my test code:
const string str = @"91023m lkajsdfl;jkasdf;piou-09324\\adf \asdf\45\ 65u\ 86\ 8\\\;";
static int testIndexOf() { return str.IndexOf('\\'); }
static int testManualIndexOf() {
string s = str;
for (int i = 0; i < s.Length; ++i)
if (s[i] == '\\') return i;
return -1;
}
I ran each method 25 million times, and measured the time using a Stopwatch
. The manual version was consistently 25% faster than the other one.
Any thoughts?!
Edit: Today I wanted to check this issue in a more comprehensive way, so I created a new project, and wrote the code you'll find below. I got the following results when running release mode from cmd for 100 million iterations:
- String. IndexOf : 00:00:07.6490042
- MyString.PublicIndexOf : 00:00:05.6676471
- MyString.InternIndexOf : 00:00:06.1191796
- MyString.PublicIndexOf2: 00:00:09.1363687
- MyString.InternIndexOf2: 00:00:09.1182569
I ran these tests at least 20 times, getting almost the same results every time. My machine is XP SP3, VS 2008 SP1, P4 3.0 GHz with no hyper-threading and 1 GB RAM. I find the results really strange. As you can see, String.IndexOf
was about 33% slower than my PublicIndexOf
. Even stranger, I wrote the same method as internal
, and it was about 8% slower than the public
one! I do not understand what's happening, and I hope you could help me understand!
The testing code is below. (Sorry for the repeated code, but I found that using a delegate showed different timings, with the public
and internal
methods taking the same time.)
public static class MyString {
public static int PublicIndexOf(string str, char value) {
for (int i = 0; i < str.Length; ++i)
if (str[i] == value) return i;
return -1;
}
internal static int InternIndexOf(string str, char value) {
for (int i = 0; i < str.Length; ++i)
if (str[i] == value) return i;
return -1;
}
public static int PublicIndexOf2(string str, char value, int startIndex) {
if (startIndex < 0 || startIndex >= str.Length)
throw new ArgumentOutOfRangeException("startIndex");
for (; startIndex < str.Length; ++startIndex)
if (str[startIndex] == value) return startIndex;
return -1;
}
internal static int InternIndexOf2(string str, char value, int startIndex) {
if (startIndex < 0 || startIndex >= str.Length)
throw new ArgumentOutOfRangeException("startIndex");
for (; startIndex < str.Length; ++startIndex)
if (str[startIndex] == value) return startIndex;
return -1;
}
}
class Program {
static void Main(string[] args) {
int iterations = 100 * 1000 * 1000; // 100 millions
char separator = '\\';
string str = @"91023m lkajsdfl;jkasdf;piou-09324\\adf \asdf\45\ 65u\ 86\ 8\\\;";
Stopwatch watch = new Stopwatch();
// test String.IndexOf
int sum = 0;
watch.Start();
for (int i = 0; i < iterations; ++i)
sum += str.IndexOf(separator);
watch.Stop();
Console.WriteLine(" String. IndexOf : ({0}, {1})", watch.Elapsed, sum);
// test MyString.PublicIndexOf
sum = 0;
watch.Reset(); watch.Start();
for (int i = 0; i < iterations; ++i)
sum += MyString.PublicIndexOf(str, separator);
watch.Stop();
Console.WriteLine("MyString.PublicIndexOf : ({0}, {1})", watch.Elapsed, sum);
// test MyString.InternIndexOf
sum = 0;
watch.Reset(); watch.Start();
for (int i = 0; i < iterations; ++i)
sum += MyString.InternIndexOf(str, separator);
watch.Stop();
Console.WriteLine("MyString.InternIndexOf : ({0}, {1})", watch.Elapsed, sum);
// test MyString.PublicIndexOf2
sum = 0;
watch.Reset(); watch.Start();
for (int i = 0; i < iterations; ++i)
sum += MyString.PublicIndexOf2(str, separator,0);
watch.Stop();
Console.WriteLine("MyString.PublicIndexOf2: ({0}, {1})", watch.Elapsed, sum);
// test MyString.InternIndexOf2
sum = 0;
watch.Reset(); watch.Start();
for (int i = 0; i < iterations; ++i)
sum += MyString.InternIndexOf2(str, separator,0);
watch.Stop();
Console.WriteLine("MyString.InternIndexOf2: ({0}, {1})", watch.Elapsed, sum);
}
}