这个问题源于我在foreach
循环上提出上一个问题的原因。我有一个大字符串数组(比如成千上万),我想迭代数组,并且能够根据某个条件突破,我需要最佳性能。
一些示例代码:
for(int i = 0; i < array.length && flag == true; i++){
//Processing and set flag
}
//..or
foreach(string item in array){
//processing...set flag
if(!flag)
break;
}
哪种方式会更便宜?
答案 0 :(得分:5)
您可以随时对它们进行基准测试。使用Stopwatch
并迭代,例如,千万次迭代,看看哪个更快。
我认为你会发现,两者几乎相同,因为JIT编译器优化数组上的foreach
基本上是for
。
flevine100实际上是正确的一般 for
稍微比foreach
更有效GetEnumerator
类型IEnumerator
1}}方法创建一个实现IEnumerator<T>
或System.Collections.Generic
的新对象(由于内存分配和方法调用开销);然而,由于IEnumerable
使用值类型枚举器(更不用说the foreach
construct does not actually require an IEnumerable
implementation in the first place)明确{{1}}实现,因此{{1}}中的大多数集合都不是这种情况。
甚至更少数组的情况,因为它们是固定大小的,因此很容易通过JIT编译器进行优化。
答案 1 :(得分:2)
我发现for(...)比foreach()更快。我认为这是因为foreach()使用IEnumerable管道。
因为你关注速度...在.NET 4.0中如果你的循环体不依赖于共享状态,你应该使用Parallel.For或Parallel.Foreach来扩展你的处理到多个处理器。
答案 2 :(得分:2)
我不会专注于这种微优化水平。
您可能有更好的优化机会,特别是如果您正在处理字符串。 for / foreach差异将是整个运行时的一小部分,它的性能基本相同。
最好让算法尽可能“干净”,并寻找其他性能机会如果需要,例如线程化整个例程。
答案 3 :(得分:1)
如果没有基准测试,如果两者之间存在显着差异,我会感到非常惊讶(当然,答案很大程度上取决于循环内的工作和集合的类型)。
根据我的经验,这些东西从未在生产代码中产生性能瓶颈。任何具有重要意义的应用程序无疑都涉及某种I/O或网络交互,这些交互占据了大部分性能损失。
如果您担心,我强烈建议您对违规代码进行分析,找出更快的代码。
答案 4 :(得分:1)
在第二个示例中,您没有早期退出子句,但添加break
代替您的旗帜将实现此目的。
我不清楚内部是什么,除了foreach使用枚举器,for循环将取决于元素访问器的可伸缩性。在列表中,一旦添加该中断,它们实际上是相等的。
答案 5 :(得分:0)
对于一个简单的裸阵列,for循环将倾向于产生略小的IL。比较
static int[] array = new int[100];
static void UseForLoop () {
for (int i = 0; i < array.Length; ++i) {
Console.WriteLine(array[i]);
}
}
static void UseForeachLoop () {
foreach (int i in array) {
Console.WriteLine(i);
}
}
从VS 2010生成以下IL集,默认发布配置:
.method private hidebysig static void UseForLoop() cil managed
{
.maxstack 2
.locals init (
[0] int32 i)
L_0000: ldc.i4.0
L_0001: stloc.0
L_0002: br.s L_0014
L_0004: ldsfld int32[] ConsoleApplication5.Program::array
L_0009: ldloc.0
L_000a: ldelem.i4
L_000b: call void [mscorlib]System.Console::WriteLine(int32)
L_0010: ldloc.0
L_0011: ldc.i4.1
L_0012: add
L_0013: stloc.0
L_0014: ldloc.0
L_0015: ldsfld int32[] ConsoleApplication5.Program::array
L_001a: ldlen
L_001b: conv.i4
L_001c: blt.s L_0004
L_001e: ret
}
.method private hidebysig static void UseForeachLoop() cil managed
{
.maxstack 2
.locals init (
[0] int32 i,
[1] int32[] CS$6$0000,
[2] int32 CS$7$0001)
L_0000: ldsfld int32[] ConsoleApplication5.Program::array
L_0005: stloc.1
L_0006: ldc.i4.0
L_0007: stloc.2
L_0008: br.s L_0018
L_000a: ldloc.1
L_000b: ldloc.2
L_000c: ldelem.i4
L_000d: stloc.0
L_000e: ldloc.0
L_000f: call void [mscorlib]System.Console::WriteLine(int32)
L_0014: ldloc.2
L_0015: ldc.i4.1
L_0016: add
L_0017: stloc.2
L_0018: ldloc.2
L_0019: ldloc.1
L_001a: ldlen
L_001b: conv.i4
L_001c: blt.s L_000a
L_001e: ret
}
..但那里的关键部分,循环,基本相同。正如其他人所说,这也是一种微观优化。来自这两种方法的JIT的x86可能会是相同的,除非你使用复杂的枚举器迭代复杂的集合,否则即使在实际的例子中差异也不大。
我会使用更具可读性的那个 - 如果速度真的那么多关注点,支持for循环,但你可能会从算法优化中获得更好的结果。< / p>