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 2: Running the tests on a different machine with .NET 4.0 yields results very similar to those in Marc Gravell’s answer. String.IndexOf(char) is faster than manual searching.
Edit: Today (Jan 4 ’09) 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); } }
(updated)
With your newly posted rig, I get the numbers below, which are in line with what I would expect:
(original)
I cannot reproduce your numbers in release at the console. I did 25M iterations, with results:
So
IndexOfis quicker. I suspect your test rig is not being run in release mode at the command line (you can’t run performance tests with the debugger attached; even the IDE adds too much overhead).Here is my rig: