在c# / VB.NET/。哪个循环运行得更快,for还是foreach?
自从很久以前我读到for循环比foreach循环工作得快,我就认为它适用于所有集合、泛型集合、所有数组等。
我搜索了谷歌,找到了几篇文章,但大多数都是不确定的(阅读文章评论),而且是开放式的。
理想的情况是列出每种情况以及最佳解决方案。
例如(这只是一个例子):
用于迭代1000+的数组
字符串- for比foreach好
对于迭代IList(非泛型)字符串- foreach更好
比
在网上找到了一些相同的参考资料:
由Emmanuel Schanzer撰写的原创文章
CodeProject FOREACH Vs. FOR
博客——去博客还是不去博客,这是个问题
ASP。NET论坛- NET 1.1 c# for vs foreach
(编辑)
除了可读性之外,我对事实和数据真的很感兴趣。在某些应用中,最后一英里的性能优化确实很重要。
Foreach循环比for循环展示了更具体的意图。
使用foreach循环向使用您代码的任何人表明,您计划对集合中的每个成员执行一些操作,而不管其在集合中的位置。它还显示您没有修改原始集合(如果您试图修改,则会抛出异常)。
foreach的另一个优点是它适用于任何IEnumerable,而as for只适用于IList,其中每个元素实际上都有一个索引。
但是,如果需要使用元素的索引,那么当然应该允许使用for循环。但是如果您不需要使用索引,那么使用索引只会使您的代码变得混乱。
据我所知,这对性能没有重大影响。在未来的某个阶段,使用foreach调整代码以在多核上运行可能会更容易,但现在还不需要担心这一点。
internal static void Test()
{
int LOOP_LENGTH = 10000000;
Random random = new Random((int)DateTime.Now.ToFileTime());
{
Dictionary<int, int> dict = new Dictionary<int, int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i, i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
for (int k = 0; k < dict.Count; k++)
{
if (dict[k] > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"Dictionary for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
Dictionary<int, int> dict = new Dictionary<int, int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i, i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
foreach (var item in dict)
{
if (item.Value > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"Dictionary foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
Dictionary<int, int> dict = new Dictionary<int, int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i, i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
foreach (var item in dict.Values)
{
if (item > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"Dictionary foreach values T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
List<int> dict = new List<int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
for (int k = 0; k < dict.Count; k++)
{
if (dict[k] > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"list for T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
{
List<int> dict = new List<int>();
long first_memory = GC.GetTotalMemory(true);
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < 64; i++)
{
dict.Add(i);
}
for (int i = 0; i < LOOP_LENGTH; i++)
{
foreach (var item in dict)
{
if (item > 1000000) Console.WriteLine("Test");
}
}
stopWatch.Stop();
var last_memory = GC.GetTotalMemory(true);
Console.WriteLine($"list foreach T:{stopWatch.Elapsed.TotalSeconds}s\t M:{last_memory - first_memory}");
GC.Collect();
}
}
T:10.1957728s M:2080的字典
字典T:10.5900586 M:1952
字典foreach值T:3.8294776s M:2088
T:3.7981471s M:320
T:4.4861377s M:648
我认为使用Parallel.ForEach()以及ConcurrentDictionary或ConcurrentBag会更快
下面是Parallel.ForEach()的例子
var primeNumbers = new ConcurrentBag<T>();
Parallel.ForEach(numbers, number =>
{
if (IsPrime(number))
{
primeNumbers.Add(number);
}
});
And
var productImage = new ConcurrentDictionary<int,ResultModel>();
Parallel.ForEach(pendingActiveImagesBatch, pictureItem =>
{
productImage.TryAdd(pictureItem.Id,pictureItem));
});
引用平行。ForEach
参考ConcurrentDictionary最后
这和大多数“哪个更快”的问题有相同的两个答案:
1)如果你不测量,你就不知道。
2)(因为…)视情况而定。
这取决于“MoveNext()”方法的代价,相对于“this[int index]”方法的代价,对于你要迭代的IEnumerable的类型(或类型)。
“foreach”关键字是一系列操作的简写——它在IEnumerable上调用GetEnumerator()一次,每次迭代调用MoveNext()一次,它做一些类型检查,等等。最可能影响性能度量的是MoveNext()的成本,因为它被调用了O(N)次。可能便宜,但也可能不便宜。
“for”关键字看起来更容易预测,但在大多数“for”循环中,你会发现类似“collection[index]”这样的东西。这看起来像是一个简单的数组索引操作,但它实际上是一个方法调用,其开销完全取决于迭代的集合的性质。可能便宜,但也可能不便宜。
如果集合的底层结构本质上是一个链表,MoveNext是非常便宜的,但是索引器可能有O(N)成本,使得“for”循环的真正成本为O(N*N)。