2

考虑一下:

必备:

//The alphabet from a-z
List<char> letterRange = Enumerable.Range('a', 'z' - 'a' + 1)
.Select(i => (Char)i).ToList(); //97 - 122 + 1 = 26 letters/iterations

标准foreach:

foreach (var range in letterRange)
{
    Console.Write(range + ",");
}
Console.Write("\n");

内置foreach:

letterRange.ForEach(range => Console.Write(range + ",")); //delegate(char range) works as well
Console.Write("\n");

我尝试过将它们相互定时,内置的 foreach 速度提高了 2 倍,这似乎很多。

我已经用谷歌搜索了,但我似乎找不到任何答案。

另外,关于:在 .NET 中,哪个循环运行得更快,“for”还是“foreach”?

for (int i = 0; i < letterRange.Count; i++)
{
    Console.Write(letterRange[i] + ",");
}
Console.Write("\n");

据我所知,它的执行速度并不比标准 foreach 快。

4

3 回答 3

17

我认为你的基准是有缺陷的。Console.Write是一项 I/O 绑定任务,它是基准测试中最耗时的部分。这是一个微基准,应该非常仔细地完成以获得准确的结果。

这是一个基准:http ://diditwith.net/PermaLink,guid,506c0888-8c5f-40e5-9d39-a09e2ebf3a55.aspx (看起来不错,但我自己还没有验证过)。截至 2015 年 8 月 14 日,该链接似乎已断开

于 2009-04-13T14:39:54.907 回答
11

当您进入一个 foreach 循环时,您将枚举每个项目。该枚举每次迭代会导致两个方法调用:一个 to IEnumerator<T>.MoveNext(),另一个 to IEnumerator<T>.Current。那是两条callIL 指令。

List<T>.ForEach更快,因为它每次迭代只有一个方法调用——无论你提供的Action<T>委托是什么。这是一条callvirtIL 指令。这比两条call指令快得多。

正如其他人指出的那样,IO 绑定指令Console.WriteLine()会污染您的基准。做一些可以完全限制在记忆中的事情,比如把一个序列的元素加在一起。

于 2009-04-13T14:45:50.727 回答
2

这是因为 foreach 方法没有使用枚举器。枚举器(foreach)往往比基本的 for 循环慢:

这是 ForEach 方法的代码:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

虽然我预计会有差异,但我有点惊讶它和你所说的一样大。使用枚举器方法,您将创建一个额外的对象,然后采取额外的步骤来确保枚举器不会失效(修改集合)。您还通过一个额外的函数调用 Current() 来获取成员。所有这些都增加了时间。

于 2009-04-13T14:39:52.987 回答