C# 为什么列表<;T>;。ForEach比标准ForEach快?

C# 为什么列表<;T>;。ForEach比标准ForEach快?,c#,.net,list,foreach,benchmarking,C#,.net,List,Foreach,Benchmarking,考虑这一点: 必要的: //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: letterRange.ForEach(range => Console.Write(range + ","));

考虑这一点:

必要的:

//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:

letterRange.ForEach(range => Console.Write(range + ",")); //delegate(char range) works as well
Console.Write("\n");
我试着对它们进行计时,内置的foreach速度快了2倍,这似乎很多

我在谷歌上搜索过,但似乎找不到任何答案

此外,关于:

for(int i=0;i

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

这是因为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]);
    }
}
public void ForEach(操作)
{
if(action==null)
{
ThrowHelper.ThrowArgumentNullException(argument.match除外);
}
对于(int i=0;i

虽然我希望会有不同,但我有点惊讶它像你说的那么大。使用枚举器方法,您将执行额外的对象创建,然后执行额外的步骤以确保枚举器不会失效(集合已修改)。您还需要通过一个额外的函数调用Current()来获取成员。所有这些都增加了时间。

我认为你的基准有缺陷<代码>控制台。写入
是一项I/O绑定任务,是基准测试中最耗时的部分。这是一个微观基准,应该非常小心地进行,以获得准确的结果


这里有一个基准:(看起来不错,但我自己还没有验证过)从2015年8月14日起,该链接似乎已断开

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

List.ForEach
更快,因为每次迭代只有一个方法调用——不管您提供的
Action
委托是什么这是一条
callvirt
IL指令。这比两个
调用
指令要快得多。


正如其他人指出的,像
Console.WriteLine()
这样的IO绑定指令会污染您的基准测试。做一些完全局限于记忆的事情,比如把序列的元素加在一起。

我相信你是对的。您是否能够提供一些适当的基准测试结果?相反,创建一个具有足够初始容量的stringbuilder来保存整个结果并将每个字符串附加到该结果。然后将所有内容都输出一次,在您停止计时之后。啊,我没有注意到这篇文章还提供了Lists.ForEach基准测试。那就谢谢你了
for (int i = 0; i < letterRange.Count; i++)
{
    Console.Write(letterRange[i] + ",");
}
Console.Write("\n");
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]);
    }
}