C# 中控制结构“for”和“foreach”的性能差异
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1124753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Performance difference for control structures 'for' and 'foreach' in C#
提问by Kthevar
Which code snippet will give better performance? The below code segments were written in C#.
哪个代码片段会提供更好的性能?下面的代码段是用 C# 编写的。
1.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2.
2.
foreach(MyType current in list)
{
current.DoSomething();
}
采纳答案by Jon Skeet
Well, it partly depends on the exact type of list
. It will also depend on the exact CLR you're using.
嗯,这部分取决于list
. 它还取决于您使用的确切 CLR。
Whether it's in any way significantor not will depend on whether you're doing any real work in the loop. In almost allcases, the difference to performance won't be significant, but the difference to readability favours the foreach
loop.
它是否以任何方式重要取决于您是否在循环中进行任何实际工作。在几乎所有情况下,性能的差异都不会很大,但可读性的差异有利于foreach
循环。
I'd personally use LINQ to avoid the "if" too:
我个人也会使用 LINQ 来避免“如果”:
foreach (var item in list.Where(condition))
{
}
EDIT: For those of you who are claiming that iterating over a List<T>
with foreach
produces the same code as the for
loop, here's evidence that it doesn't:
编辑:对于那些声称迭代List<T>
withforeach
产生与for
循环相同的代码的人,这里有证据表明它没有:
static void IterateOverList(List<object> list)
{
foreach (object o in list)
{
Console.WriteLine(o);
}
}
Produces IL of:
产生以下 IL:
.method private hidebysig static void IterateOverList(class [mscorlib]System.Collections.Generic.List`1<object> list) cil managed
{
// Code size 49 (0x31)
.maxstack 1
.locals init (object V_0,
valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> V_1)
IL_0000: ldarg.0
IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<object>::GetEnumerator()
IL_0006: stloc.1
.try
{
IL_0007: br.s IL_0017
IL_0009: ldloca.s V_1
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current()
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: call void [mscorlib]System.Console::WriteLine(object)
IL_0017: ldloca.s V_1
IL_0019: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext()
IL_001e: brtrue.s IL_0009
IL_0020: leave.s IL_0030
} // end .try
finally
{
IL_0022: ldloca.s V_1
IL_0024: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>
IL_002a: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002f: endfinally
} // end handler
IL_0030: ret
} // end of method Test::IterateOverList
The compiler treats arraysdifferently, converting a foreach
loop basically to a for
loop, but not List<T>
. Here's the equivalent code for an array:
编译器以不同的方式处理数组,foreach
基本上将for
循环转换为循环,但不会将List<T>
. 这是数组的等效代码:
static void IterateOverArray(object[] array)
{
foreach (object o in array)
{
Console.WriteLine(o);
}
}
// Compiles into...
.method private hidebysig static void IterateOverArray(object[] 'array') cil managed
{
// Code size 27 (0x1b)
.maxstack 2
.locals init (object V_0,
object[] V_1,
int32 V_2)
IL_0000: ldarg.0
IL_0001: stloc.1
IL_0002: ldc.i4.0
IL_0003: stloc.2
IL_0004: br.s IL_0014
IL_0006: ldloc.1
IL_0007: ldloc.2
IL_0008: ldelem.ref
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: call void [mscorlib]System.Console::WriteLine(object)
IL_0010: ldloc.2
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: stloc.2
IL_0014: ldloc.2
IL_0015: ldloc.1
IL_0016: ldlen
IL_0017: conv.i4
IL_0018: blt.s IL_0006
IL_001a: ret
} // end of method Test::IterateOverArray
Interestingly, I can't find this documented in the C# 3 spec anywhere...
有趣的是,我在 C# 3 规范中的任何地方都找不到这个文档......
回答by Charles Prakash Dasari
Like other people have mentioned though the performance doesn't actually matter much, the foreach will always be a little bit slower because of the IEnumerable
/IEnumerator
usage in the loop. The compiler translates the construct into calls on that interface and for every step a function + a property are called in the foreach construct.
就像其他人提到的,虽然性能实际上并不重要,但由于循环中的IEnumerable
/IEnumerator
使用,foreach 总是会慢一点。编译器将构造转换为对该接口的调用,并且对于每一步,在 foreach 构造中都会调用一个函数 + 一个属性。
IEnumerator iterator = ((IEnumerable)list).GetEnumerator();
while (iterator.MoveNext()) {
var item = iterator.Current;
// do stuff
}
This is the equivalent expansion of the construct in C#. You can imagine how the performance impact can vary based on the implementations of MoveNext and Current. Whereas in an array access, you don't have that dependencies.
这是 C# 中构造的等效扩展。您可以想象性能影响如何根据 MoveNext 和 Current 的实现而变化。而在数组访问中,您没有这种依赖关系。
回答by Kenny Mann
An easy test to semi-validate. I did a small test, just to see. Here is the code:
一个简单的半验证测试。我做了一个小测试,只是为了看看。这是代码:
static void Main(string[] args)
{
List<int> intList = new List<int>();
for (int i = 0; i < 10000000; i++)
{
intList.Add(i);
}
DateTime timeStarted = DateTime.Now;
for (int i = 0; i < intList.Count; i++)
{
int foo = intList[i] * 2;
if (foo % 2 == 0)
{
}
}
TimeSpan finished = DateTime.Now - timeStarted;
Console.WriteLine(finished.TotalMilliseconds.ToString());
Console.Read();
}
And here is the foreach section:
这是 foreach 部分:
foreach (int i in intList)
{
int foo = i * 2;
if (foo % 2 == 0)
{
}
}
When I replaced the for with a foreach -- the foreach was 20 milliseconds faster -- consistently. The for was 135-139ms while the foreach was 113-119ms. I swapped back and forth several times, making sure it wasn't some process that just kicked in.
当我用 foreach 替换 for 时——foreach 快了 20 毫秒——始终如一。for 是 135-139ms 而 foreach 是 113-119ms。我来回交换了几次,以确保这不是某个刚刚开始的过程。
However, when I removed the foo and the if statement, the for was faster by 30 ms (foreach was 88ms and for was 59ms). They were both empty shells. I'm assuming the foreach actually passed a variable where as the for was just incrementing a variable. If I added
但是,当我删除 foo 和 if 语句时,for 快了 30 毫秒(foreach 是 88 毫秒,for 是 59 毫秒)。它们都是空壳。我假设 foreach 实际上传递了一个变量,而 for 只是增加了一个变量。如果我加了
int foo = intList[i];
Then the for become slow by about 30ms. I'm assuming this had to do with it creating foo and grabbing the variable in the array and assigning it to foo. If you just access intList[i] then you don't have that penalty.
然后 for 变慢了大约 30 毫秒。我假设这与它创建 foo 并获取数组中的变量并将其分配给 foo 有关。如果您只是访问 intList[i] ,那么您不会受到惩罚。
In all honesty.. I expected the foreach to be slightly slower in all circumstances, but not enough to matter in most applications.
老实说..我预计foreach在所有情况下都会稍微慢一点,但在大多数应用程序中还不够重要。
edit: here is the new code using Jons suggestions (134217728 is the biggest int you can have before System.OutOfMemory exception gets thrown):
编辑:这是使用 Jons 建议的新代码(134217728 是在抛出 System.OutOfMemory 异常之前可以拥有的最大整数):
static void Main(string[] args)
{
List<int> intList = new List<int>();
Console.WriteLine("Generating data.");
for (int i = 0; i < 134217728 ; i++)
{
intList.Add(i);
}
Console.Write("Calculating for loop:\t\t");
Stopwatch time = new Stopwatch();
time.Start();
for (int i = 0; i < intList.Count; i++)
{
int foo = intList[i] * 2;
if (foo % 2 == 0)
{
}
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds.ToString() + "ms");
Console.Write("Calculating foreach loop:\t");
time.Reset();
time.Start();
foreach (int i in intList)
{
int foo = i * 2;
if (foo % 2 == 0)
{
}
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds.ToString() + "ms");
Console.Read();
}
And here are the results:
结果如下:
Generating data. Calculating for loop: 2458ms Calculating foreach loop: 2005ms
生成数据。计算 for 循环:2458ms 计算 foreach 循环:2005ms
Swapping them around to see if it deals with the order of things yields the same results (nearly).
交换它们以查看它是否处理事物的顺序会产生相同的结果(几乎)。
回答by Tom Lokhorst
Note: this answer applies more to Java than it does to C#, since C# doesn't have an indexer on LinkedLists
, but I think the general point still holds.
注意:这个答案更适用于 Java 而不是 C#,因为 C# 在 上没有索引器LinkedLists
,但我认为一般观点仍然成立。
If the list
you're working with happens to be a LinkedList
, the performance of the indexer-code (array-styleaccessing) is a lot worse than using the IEnumerator
from the foreach
, for large lists.
如果list
您正在使用的 恰好是 a LinkedList
,则索引器代码(数组样式访问)的性能比使用IEnumerator
from 的foreach
, 大列表差很多。
When you access element 10.000 in a LinkedList
using the indexer syntax: list[10000]
, the linked list will start at the head node, and traverse the Next
-pointer ten thousand times, until it reaches the correct object. Obviously, if you do this in a loop, you will get:
当您LinkedList
使用索引器语法访问 a 中的元素 10.000 时:list[10000]
,链表将从头节点开始,并遍历 -Next
指针一万次,直到到达正确的对象。显然,如果你在循环中这样做,你会得到:
list[0]; // head
list[1]; // head.Next
list[2]; // head.Next.Next
// etc.
When you call GetEnumerator
(implicitly using the forach
-syntax), you'll get an IEnumerator
object that has a pointer to the head node. Each time you call MoveNext
, that pointer is moved to the next node, like so:
当您调用GetEnumerator
(隐式使用forach
-syntax)时,您将获得一个IEnumerator
具有指向头节点的指针的对象。每次调用时MoveNext
,该指针都会移动到下一个节点,如下所示:
IEnumerator em = list.GetEnumerator(); // Current points at head
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
em.MoveNext(); // Update Current to .Next
// etc.
As you can see, in the case of LinkedList
s, the array indexer method becomes slower and slower, the longer you loop (it has to go through the same head pointer over and over again). Whereas the IEnumerable
just operates in constant time.
如您所见,在LinkedList
s的情况下,数组索引器方法变得越来越慢,循环时间越长(它必须一遍又一遍地通过同一个头指针)。而IEnumerable
just 在恒定时间内运行。
Of course, as Jon said this really depends on the type of list
, if the list
is not a LinkedList
, but an array, the behavior is completely different.
当然,正如 Jon 所说,这实际上取决于 的类型list
,如果list
不是LinkedList
,而是数组,则行为完全不同。
回答by Martin Brown
A for
loop gets compiled to code approximately equivalent to this:
一个for
循环被编译成与此大致等效的代码:
int tempCount = 0;
while (tempCount < list.Count)
{
if (list[tempCount].value == value)
{
// Do something
}
tempCount++;
}
Where as a foreach
loop gets compiled to code approximately equivalent to this:
当一个foreach
循环被编译成与此大致等效的代码时:
using (IEnumerator<T> e = list.GetEnumerator())
{
while (e.MoveNext())
{
T o = (MyClass)e.Current;
if (row.value == value)
{
// Do something
}
}
}
So as you can see, it would all depend upon how the enumerator is implemented versus how the lists indexer is implemented. As it turns out the enumerator for types based on arrays are normally written something like this:
如您所见,这完全取决于枚举器的实现方式与列表索引器的实现方式。事实证明,基于数组的类型的枚举器通常是这样写的:
private static IEnumerable<T> MyEnum(List<T> list)
{
for (int i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
So as you can see, in this instance it won't make very much difference, however the enumerator for a linked list would probably look something like this:
正如你所看到的,在这种情况下,它不会有太大的不同,但是链表的枚举器可能看起来像这样:
private static IEnumerable<T> MyEnum(LinkedList<T> list)
{
LinkedListNode<T> current = list.First;
do
{
yield return current.Value;
current = current.Next;
}
while (current != null);
}
In .NETyou will find that the LinkedList<T> class does not even have an indexer, so you wouldn't be able to do your for loop on a linked list; but if you could, the indexer would have to be written like so:
在.NET 中,您会发现 LinkedList<T> 类甚至没有索引器,因此您将无法在链表上执行 for 循环;但是如果可以的话,索引器必须这样写:
public T this[int index]
{
LinkedListNode<T> current = this.First;
for (int i = 1; i <= index; i++)
{
current = current.Next;
}
return current.value;
}
As you can see, calling this multiple times in a loop is going to be much slower than using an enumerator that can remember where it is in the list.
如您所见,在循环中多次调用它比使用可以记住它在列表中的位置的枚举器慢得多。
回答by Sam
There is a further interesting fact which can be easy missed when testing the speed of both loops: Using the debug mode doesn't let the compiler optimize the code using the default settings.
在测试两个循环的速度时,还有一个很容易被忽略的有趣事实:使用调试模式不会让编译器使用默认设置优化代码。
This led me to the interesting result that the foreach is faster than for in the debug mode. Whereas the for ist faster than foreach in the release mode. Obviously the compiler has better ways to optimize a for loop than a foreach loop which compromises several method calls. A for loop is by the way such fundamental that it's possible that this is even optimized by the CPU itself.
这让我得到了一个有趣的结果,即在调试模式下 foreach 比 for 快。而在发布模式下 forist 比 foreach 更快。显然,编译器有更好的方法来优化 for 循环,而不是影响多个方法调用的 foreach 循环。顺便说一句,for 循环是如此基础,以至于它甚至可能由 CPU 本身进行优化。
回答by ThunderGr
After reading enough arguments that "the foreach loop should be preferred for readability", I can say that my first reaction was "what"? Readability, in general, is subjective and, in this particular instance, even more. For someone with a background in programming(practically, every language before Java), for loops are much easier to read than foreach loops. In addition, the same people claiming that foreach loops are more readable, are also supporters of linq and other "features" that make code hard to read and maintain, something that proves the above point.
在阅读了足够多的关于“foreach 循环应该优先用于可读性”的论点之后,我可以说我的第一反应是“什么”?一般来说,可读性是主观的,在这种特殊情况下,甚至更多。对于有编程背景的人(实际上,Java 之前的所有语言),for 循环比 foreach 循环更容易阅读。此外,那些声称 foreach 循环更具可读性的人,也是 linq 和其他使代码难以阅读和维护的“功能”的支持者,这证明了上述观点。
About the impact on performance, see the answer to thisquestion.
关于对性能的影响,请参阅此问题的答案。
EDIT: There are collections in C#(like the HashSet) that have no indexer. In these collections, foreachis the only way to iterate and it is the only case I think it should be used over for.
编辑:C# 中的集合(如 HashSet)没有索引器。在这些集合中,foreach是唯一的迭代方式,也是我认为它应该用于.
回答by rpax
In the example you provided, it is definitely better to use foreach
loop instead a for
loop.
在您提供的示例中,最好使用foreach
循环而不是for
循环。
The standard foreach
construct can be faster (1,5 cycles per step) than a simple for-loop
(2 cycles per step), unless the loop has been unrolled (1.0 cycles per step).
除非循环已展开(每步 1.0 个循环),否则标准foreach
构造(每步 1.5 个循环)比简单构造(每步for-loop
2 个循环)更快。
So for everyday code, performance is not a reason to use the more complex for
, while
or do-while
constructs.
因此,对于日常代码,性能不是使用更复杂的for
,while
或do-while
构造的理由。
Check out this link: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
查看此链接:http: //www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
╔══════════════════════╦═══════════╦═══════╦════════════════════════╦═════════════════════╗
║ Method ║ List<int> ║ int[] ║ Ilist<int> onList<Int> ║ Ilist<int> on?int[] ║
╠══════════════════════╬═══════════╬═══════╬════════════════════════╬═════════════════════╣
║ Time (ms) ║ 23,80 ║ 17,56 ║ 92,33 ║ 86,90 ║
║ Transfer rate (GB/s) ║ 2,82 ║ 3,82 ║ 0,73 ║ 0,77 ║
║ % Max ║ 25,2% ║ 34,1% ║ 6,5% ║ 6,9% ║
║ Cycles / read ║ 3,97 ║ 2,93 ║ 15,41 ║ 14,50 ║
║ Reads / iteration ║ 16 ║ 16 ║ 16 ║ 16 ║
║ Cycles / iteration ║ 63,5 ║ 46,9 ║ 246,5 ║ 232,0 ║
╚══════════════════════╩═══════════╩═══════╩════════════════════════╩═════════════════════╝
回答by Or Yaacov
you can read about it in Deep .NET - part 1 Iteration
你可以在Deep .NET - part 1 Iteration 中阅读它
it's cover the results (without the first initialization) from .NET source code all the way to the disassembly.
它涵盖了从 .NET 源代码一直到反汇编的结果(没有第一次初始化)。
for example - Array Iteration with a foreach loop: