简体   繁体   English

为什么是List <T> .ForEach比标准foreach更快?

[英]Why is List<T>.ForEach faster than standard foreach?

Consider this: 考虑一下:

Requisite: 必要的:

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

Standard foreach: 标准foreach:

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

Inbuilt foreach: 内置的foreach:

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

I have tried timing them against each other and the inbuilt foreach is up to 2 times faster, which seems like a lot. 我已经尝试过将它们相互对准,并且内置的foreach速度提高了2倍,这似乎很多。

I have googled around, but I can not seem to find any answers. 我用Google搜索了,但我似乎无法找到任何答案。

Also, regarding: In .NET, which loop runs faster, 'for' or 'foreach'? 另外,关于: 在.NET中,哪个循环运行得更快,'for'或'foreach'?

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

Doesn't act execute faster than standard foreach as far as I can tell. 据我所知,不执行比标准foreach更快。

I think your benchmark is flawed. 我认为你的基准是有缺陷的。 Console.Write is an I/O bound task and it's the most time consuming part of your benchmark. Console.Write是一个I / O绑定任务,它是您的基准测试中最耗时的部分。 This is a micro-benchmark and should be done very carefully for accurate results. 这是一个微观基准,应该非常仔细地进行,以获得准确的结果。

Here is a benchmark: http://diditwith.net/PermaLink,guid,506c0888-8c5f-40e5-9d39-a09e2ebf3a55.aspx (It looks good but I haven't validated it myself). 这是一个基准: http//diditwith.net/PermaLink,guid,506c0888-8c5f-40e5-9d39-a09e2ebf3a55.aspx (看起来不错,但我自己没有验证过)。 The link appears to be broken as of 8/14/2015 截至2015年8月14日,该链接似乎已被破坏

When you enter a foreach loop, you enumerate over each item. 当您输入foreach循环时,您将枚举每个项目。 That enumeration causes two method calls per iteration: one to IEnumerator<T>.MoveNext() , and another to IEnumerator<T>.Current . 该枚举导致每次迭代两次方法调用:一次到IEnumerator<T>.MoveNext() ,另一次到IEnumerator<T>.Current That's two call IL instructions. 这是两个call IL指令。

List<T>.ForEach is faster because it has only one method call per iteration -- whatever your supplied Action<T> delegate is. List<T>.ForEach更快,因为它每次迭代只有一个方法调用 - 无论你提供的Action<T>委托是什么。 That's one callvirt IL instruction. 这是一个callvirt IL指令。 This is significantly faster than two call instructions. 这明显快于两个call指令。

As others pointed out, IO-bound instructions like Console.WriteLine() will pollute your benchmark. 正如其他人所指出的那样,像Console.WriteLine()这样的IO绑定指令会污染你的基准测试。 Do something that can be confined entirely to memory, like adding elements of a sequence together. 做一些可以完全局限于记忆的事情,比如将序列的元素添加到一起。

This is because the foreach method is not using an enumerator. 这是因为foreach方法没有使用枚举器。 Enumerators (foreach) tend to be slower then a basic for loop: 枚举器(foreach)往往比基本for循环慢:

Here's the code for the ForEach method: 这是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]);
    }
}

While I would expect there to be a difference, I'm a little surprised it's as large as you indicated. 虽然我希望有所不同,但我有点惊讶它和你指出的一样大。 Using the enumerator approach you are taking an extra object creation, and then extra steps are taken to ensure the enumerator is not invalidated (the collection is modified). 使用枚举器方法,您将创建额外的对象,然后采取额外的步骤以确保枚举器不会失效(集合已被修改)。 Your also going through an extra function call Current() to get the member. 您还需要通过一个额外的函数调用Current()来获取该成员。 All this adds time. 这一切都增加了时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM