在进行简单的性能测量时,我惊讶地发现调用String.IndexOf(char)
实际上比手动调用慢!这是真的吗?!这是我的测试代码:
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; }
我运行了每种方法2500万次,并使用a测量时间Stopwatch
.手动版本的速度始终比另一个版本快25%.
有什么想法吗?!
编辑2:使用.NET 4.0在另一台机器上运行测试产生的结果与Marc Gravell的答案非常相似.比手动搜索更快.String.IndexOf(char)
编辑:今天(2009年1月4日)我想以更全面的方式检查这个问题,所以我创建了一个新项目,并编写了您将在下面找到的代码.从cmd运行发布模式进行1亿次迭代时,我得到以下结果:
- 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
我运行这些测试至少20次,每次都得到几乎相同的结果.我的机器是XP SP3,VS 2008 SP1,P4 3.0 GHz,没有超线程和1 GB RAM.我发现结果真的很奇怪.如你所见,String.IndexOf
比我的慢约33%PublicIndexOf
.更奇怪的是,我写了同样的方法internal
,它比public
一个慢了大约8%!我不明白发生了什么,我希望你能帮助我理解!
测试代码如下.(对不起重复的代码,但我发现使用委托显示了不同的时间,public
并且internal
方法花费了相同的时间.)
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); } }
PEZ.. 8
即使它"手动"更快地做到这一点也不明智.今天,在您的.net版本中,您的CLR设置可能更快.也许.至少对于那个特定的字符串.但是你可以非常肯定内置的内容随着时间的推移会有更好的机会.用你的语言.IndexOf也更加清晰.
即使它"手动"更快地做到这一点也不明智.今天,在您的.net版本中,您的CLR设置可能更快.也许.至少对于那个特定的字符串.但是你可以非常肯定内置的内容随着时间的推移会有更好的机会.用你的语言.IndexOf也更加清晰.
也许这25%是调用功能的成本.
您的方法(testManualIndexOf)不会调用任何函数.
您的测试不是真正的测试,因为您没有实现相同的功能:
您没有将任何参数传递给您的手动测试,而是使用const引用,这需要执行比在真实IndexOf
方法中传递参数时更少的操作码.
您的IndexOf
测试实际上是两个方法调用而不是一个.在调试器下运行的Release版本中可能会内联外部方法调用,但是您的测试并未指示运行它的条件(如果是这种情况,那么您可以忽略这一点).
但基本上,鉴于你的方法做得少,我并不惊讶它跑得更快.
(更新)
使用新发布的装备,我得到下面的数字,这符合我的预期:
String. IndexOf : (00:00:06.3134669, -994967296) MyString.PublicIndexOf : (00:00:07.0769368, -994967296) MyString.InternIndexOf : (00:00:08.3463652, -994967296) MyString.PublicIndexOf2: (00:00:12.0049268, -994967296) MyString.InternIndexOf2: (00:00:12.4344756, -994967296)
(原版的)
我无法在控制台发布时重现您的号码.我做了25M次迭代,结果如下:
1556ms,(825000000)(testIndexOf)
1798ms,(825000000)(testManualIndexOf)
所以IndexOf
更快.我怀疑你的测试装备没有在命令行中以发布模式运行(你无法在连接调试器的情况下运行性能测试;即使IDE增加了太多的开销).
这是我的装备:
static void Main() { const int LOOP = 25000000; int chk1 = 0, chk2 = 0; Stopwatch watch1 = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { chk1 += testIndexOf(); } watch1.Stop(); Stopwatch watch2 = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { chk2 += testManualIndexOf(); } watch2.Stop(); Console.WriteLine("{0}ms, ({1})", watch1.ElapsedMilliseconds, chk1); Console.WriteLine("{0}ms, ({1})", watch2.ElapsedMilliseconds, chk2); }
您的测试或您的环境存在明确的错误.
我刚刚运行此代码:
static void Main(string[] args) { { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 25000000; i++) testIndexOf(); sw.Stop(); Console.WriteLine(sw.Elapsed); //2.27 s } { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 25000000; i++) testManualIndexOf(); sw.Stop(); Console.WriteLine(sw.Elapsed); //9.81 s } Console.ReadLine(); }
在两台不同的机器上,一台Vista,一台Linux/mono,发现内部IndexOf快了4倍.