Kth*_*var 104 c# performance foreach for-loop
哪个代码段会提供更好的性能?以下代码段是用C#编写的.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
2.
foreach(MyType current in list)
{
current.DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 130
嗯,这在一定程度上取决于确切的类型list
.它还取决于您使用的确切CLR.
它是否具有重要意义取决于你是否在循环中做任何实际的工作.在几乎所有情况下,性能的差异都不会很大,但可读性的差异有利于foreach
循环.
我个人使用LINQ来避免"if":
foreach (var item in list.Where(condition))
{
}
Run Code Online (Sandbox Code Playgroud)
编辑:对于那些你谁是声称迭代一个List<T>
与foreach
产生相同的代码for
回路,这里的证据表明,它并不:
static void IterateOverList(List<object> list)
{
foreach (object o in list)
{
Console.WriteLine(o);
}
}
Run Code Online (Sandbox Code Playgroud)
产生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
Run Code Online (Sandbox Code Playgroud)
编译器以不同方式处理数组,将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
Run Code Online (Sandbox Code Playgroud)
有趣的是,我无法在任何地方找到C#3规范中记录的内容......
Mar*_*own 14
一个for
循环被编译成大致相当于此的代码:
int tempCount = 0;
while (tempCount < list.Count)
{
if (list[tempCount].value == value)
{
// Do something
}
tempCount++;
}
Run Code Online (Sandbox Code Playgroud)
凡为foreach
循环被编译的代码大约相当于这个:
using (IEnumerator<T> e = list.GetEnumerator())
{
while (e.MoveNext())
{
T o = (MyClass)e.Current;
if (row.value == value)
{
// Do something
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这将取决于枚举器的实现方式与列表索引器的实现方式.事实证明,基于数组的类型的枚举器通常写成如下:
private static IEnumerable<T> MyEnum(List<T> list)
{
for (int i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,在这种情况下它不会产生太大的区别,但是链表的枚举器可能看起来像这样:
private static IEnumerable<T> MyEnum(LinkedList<T> list)
{
LinkedListNode<T> current = list.First;
do
{
yield return current.Value;
current = current.Next;
}
while (current != null);
}
Run Code Online (Sandbox Code Playgroud)
在.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;
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,在循环中多次调用它会比使用能够记住它在列表中的位置的枚举器慢得多.
Ken*_*ann 12
一个简单的测试半验证.我做了一个小测试,只是为了看.这是代码:
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();
}
Run Code Online (Sandbox Code Playgroud)
以下是foreach部分:
foreach (int i in intList)
{
int foo = i * 2;
if (foo % 2 == 0)
{
}
}
Run Code Online (Sandbox Code Playgroud)
当我用foreach替换for时 - foreach的速度提高了20毫秒 - 始终如一.for为135-139ms,而foreach为113-119ms.我来回交换几次,确保它不是一些刚开始的过程.
但是,当我删除了foo和if语句时,for的速度提高了30 ms(foreach为88ms,为59ms).他们都是空壳.我假设foreach实际上传递了一个变量,因为for只是递增一个变量.如果我加了
int foo = intList[i];
Run Code Online (Sandbox Code Playgroud)
然后,变为慢约30ms.我假设这与创建foo并抓取数组中的变量并将其分配给foo有关.如果您只是访问intList [i],那么您没有那个惩罚.
老实说..我希望foreach在所有情况下都会稍微慢一些,但在大多数应用程序中都不够重要.
编辑:这是使用Jons建议的新代码(134217728是在抛出System.OutOfMemory异常之前可以拥有的最大的int):
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();
}
Run Code Online (Sandbox Code Playgroud)
以下是结果:
生成数据.计算循环:2458ms计算foreach循环:2005ms
交换它们以查看它是否处理事物的顺序产生相同的结果(差不多).
注意:这个答案更适用于Java而不是C#,因为C#没有开启索引器LinkedLists
,但我认为一般的观点仍然存在.
如果list
您正在使用的恰好是a LinkedList
,则索引器代码(数组样式访问)的性能比使用IEnumerator
from foreach
对于大型列表要差很多.
当您LinkedList
使用索引器语法访问元素10.000时list[10000]
,链接列表将从头节点开始,并遍历Next
-pointer一万次,直到它到达正确的对象.显然,如果你在循环中执行此操作,您将获得:
list[0]; // head
list[1]; // head.Next
list[2]; // head.Next.Next
// etc.
Run Code Online (Sandbox Code Playgroud)
当您调用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.
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,在LinkedList
s 的情况下,数组索引器方法变得越来越慢,循环的时间越长(它必须一遍又一遍地通过相同的头指针).而IEnumerable
正义在不断的时间运作.
当然,正如Jon所说,这实际上取决于类型list
,如果list
不是a LinkedList
,而是数组,那么行为就完全不同了.
归档时间: |
|
查看次数: |
43962 次 |
最近记录: |