【发布时间】:2016-02-27 04:28:16
【问题描述】:
我需要使用 C# 快速处理大量字符串。为了找到最快的方法来做到这一点,我一直在使用以下基准测试功能:
delegate void Test();
static void time(Test test, int iter, string label)
{
Stopwatch timer = new Stopwatch();
timer.Reset();
timer.Start();
int i = 0;
while (i < iter)
{
test();
i++;
}
Console.WriteLine(label + ": " + timer.ElapsedMilliseconds.ToString());
timer.Reset();
}
当我运行这段代码时:
int iter = 10000000;
string[] array = new string[] { "cat", "dog", "horse", "cow", "dimorphodon", "a", "a", "dog", "horse", "cow", "dimorphodon", "a", "a", "pig" };
List<string> list = new List<string>(array);
time(() => { int i = 0; while (i < array.Length) { if (array[i] == "cat") { return; } i++; } return; }, iter, "array search near ");
time(() => { int i = 0; while (i < array.Length) { if (array[i] == "pig") { return; } i++; } return; }, iter, "array search far ");
time(() => { int i = Array.IndexOf(array, "cat"); }, iter, "array IndexOf near ");
time(() => { int i = Array.IndexOf(array, "pig"); }, iter, "array IndexOf far ");
time(() => { list.Contains("cat"); }, iter, "list contains near ");
time(() => { list.Contains("pig"); }, iter, "list contains far ");
time(() => { int i = list.IndexOf("cat"); }, iter, "list IndexOf near ");
time(() => { int i = list.IndexOf("pig"); }, iter, "list IndexOf far ");
time(() => { int i = 0; while (i < list.Count) { if (list[i] == "cat") { return; } i++; } return; }, iter, "list search near ");
time(() => { int i = 0; while (i < list.Count) { if (list[i] == "pig") { return; } i++; } return; }, iter, "list search far ");
启用优化后,我一直认为迭代搜索数组是最快的选择,搜索列表的速度会稍微慢一些,而 List.IndexOf 和 Array.IndexOf 的速度要慢得多(3-列表前部附近的值慢 4 倍,更高的索引差距缩小,在 20-30 个元素附近达到慢 2 倍)和List.Contains 是最慢的(比IndexOf 慢约 1.2 倍,与或没有优化)。
我看到了一些关于为什么 Contains 可能比迭代搜索慢的讨论here(Contains 是通用实现的,因此创建了一个在处理字符串时不需要的通用 EqualityComparer 对象),但是尽管 Contains 的实现之间存在差异并且IndexOf 在here 上进行了详细说明,查看实现似乎只是确认包含应该是相同的(它们都创建了一个通用的 EqualityComparer 并使用它来比较列表内部数组的元素与参数)。
这表明问题可能出在我的基准测试功能上。 Contains 和 IndexOf 之间的差异是否存在一些我遗漏的元素,或者我的基准测试功能有问题?
编辑:我现在确定我的基准测试存在问题。执行一组略有不同的测试:
time(() => { list.Contains("cat"); }, iter, "list short contains ");
time(() => { list.Contains("cat"); }, iter, "list short indexof ");
time(() => { list.Contains("cat"); }, iter*10, "list long contains ");
time(() => { list.Contains("cat"); }, iter*10, "list long indexof ");
time(() => { list.Contains("cow"); }, iter, "list short contains ");
time(() => { list.Contains("cow"); }, iter, "list short indexof ");
time(() => { list.Contains("cow"); }, iter * 10, "list long contains ");
time(() => { list.Contains("c"); }, iter * 10, "list long indexof ");
导致完全不同的时间, indexof 和 contains 执行得更接近。我不知道这如何或为什么会成为可能。
再次编辑:为了清楚起见,我几乎可以肯定计时的怪异与我对计时功能的实现有关,尽管它可能与优化器有关。我经常看到, List.Contains 比 List.IndexOf 慢,但并非总是如此,并且以某种方式改变我测量时间的顺序会给我带来不同的结果。我的问题是,是什么导致上述时间不一致?
【问题讨论】:
-
@Sakura 好吧,进一步的测试证明了你的答案是正确的,尽管我仍然不知道为什么。
-
IndexOf和Contains都具有基本相同的实现,因此不应在任何不同的时间内运行。它们都使用EqualityComparer<T>.Default,而您的迭代方法没有。 -
你的问题到底是什么?
-
P... 我再次测试并发现
IndexOf在cat和pig两种情况下都更快。此外,要删除操作的第一个初始时间,请将其称为创建秒表,即test(); Stopwatch timer = new Stopwatch(); -
@Trauer 我已经更改了标题并添加了另一个编辑,希望能更清楚地说明这一点。
标签: c# performance list benchmarking