博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
foreach vs List<T>.Foreach
阅读量:5015 次
发布时间:2019-06-12

本文共 2493 字,大约阅读时间需要 8 分钟。

原文:

今天当我用foreach循环迭代一个List<int>时,我发现我变得更加了解性能问题,而以前我会去迭代一个int的ArrayList,我对此感到一点沾沾自喜。得益于泛型所带来的好处,C#编译器可以用System.Collections.Generics.IEnumerator<int>避免大量的装箱(boxing)操作,相比使用老式的System.Collections.IEnumerator。我开始想:这真的是最快的方式吗?在经过一番调查研究后,这(foreach)还不是最快的方式。

.NET 2.0发布了一些小的nuggets,可以使得我们更容易地编写代码。我最喜欢的当属Array和List<T>说添加的额外方法,这些方法接受Action<T>,Converter<TInput, TOutput>和Predicate<T>作为泛型delegate。事实上,我对这些东西还是很着迷的。当这些方法与匿名方法和lambda表达式结合使用时将发挥巨大作用。

我感兴趣的一个特别的方法是List<T>.ForEach(Action<T>)。我很好奇,ForEach调用比一个标准的foreach-loop要来的快还是慢,还是一样快?考虑如下代码:

 

long Sum(List<int> intList) { long result = 0; foreach (int i in intList) result += i; return result; } C#编译器会为上面的代码生成类似如下的伪代码:

 

 

long Sum(List<int> intList) { long result = 0; List<T>.Enumerator enumerator = intList.GetEnumerator(); try { while (enumerator.MoveNext()) { int i = enumerator.Current; result += i; } } finally { enumerator.Dispose(); } return result; }事实上,C#编译器为每次迭代生成了2个方法调用:IEnumerator<T>.MoveNext()和IEnumerator<T>.Current。List<T>.Enumerator结构(用IEnumerator实现)允许编译器生成
call IL指令而不是
callvirt ,这会有一点性能提升。相反,考虑如下代码:

 

 

long Sum(List<int> intList) { long result = 0; intList.ForEach(delegate(int i) { result += i; }); result result; }或者,等价的lambda表达式:

 

 

long Sum(List<int> intList) { long result = 0; intList.ForEach(i => result += i); return result; }使用List<T>.ForEach只会导致每次迭代中使用1个方法调用:无论你提供了什么样的Action<T>委托。这会被用callvirt IL指令调用,但是2个
call指令(一个MoveNext和一个Current)应该比一个
callvirt指令慢。所以我期望是List<T>.ForEach会更快一个(比foreach)。

 

有了这些假设,我创建了一个小的console程序,用以下4中不同的方法来迭代一个List<int>实例,并求和:

 

  1. 用for-loop迭代:for (int i = 0; i < List<int>.Count; ++i)
  2. 用for-loop,但是不调用Count:for (int i = 0; i < NUM_ITEMS; ++i)
  3. 用foreach-loop:foreach (int i in List<T>)
  4. 用List<int>.ForEach来迭代:List<int>.ForEach(delegate(int i) { result += i; })

 

首先,测试没有开启优化情况:

这些结果令人难以想象。结果,当不开启编译器优化,List<int>.ForEach比一个for-loop还要快!foreach和for-loop几乎同样快。所以如果你在没有开启优化的情况下编译你的程序,List<int>.ForEach是最快的方式。

接下来,我开启编译器优化来获得一个比较真实的结果:

看这些数字,编译器对for-loop优化超过50%而印象深刻。foreach-loop同样也获得了大约20%的提升。List<int>.ForEach没有获得很多的优化,但是需要注意,ForEach依然比foreach-loop来的快很多。List<T>.ForEach比标准的foreach-loop来的快

注意,这些测试在一台Dell Inspiron 9400的笔记本上运行,CPU是Core Duo T2400,2GB内存。如果你想要自己测试一些结果,你可以下载

一幅图片胜过千言万语,我生成了一个图表来展示不同迭代之间的速度差异。图表中显示了5个不同的取样,分别从10,000,000到50,000,000次迭代。

未启编译器优化:

开启编译器优化:

最终观点:虽然ForEach方法在迭代List<T>是非常快,但是碰到数组时就不一样了。一维数组没有ForEach方法,而且用ForEach比用foreach来的慢很多。原因是编译器没有为foreach 迭代数组生成IEnumerator<T>代码。用foreach来迭代数组,不会有方法调用,但是Array.ForEach还是会为每次迭代调用一次委托(一个callvirt调用?)。

转载于:https://www.cnblogs.com/Ruiky/archive/2012/05/09/2491237.html

你可能感兴趣的文章
题解 P1004 方格取数
查看>>
CF620E New Year Tree 状压+线段树(+dfs序?)
查看>>
ci框架数据库相关函数
查看>>
springboot aop 自定义注解
查看>>
new/delete 和malloc/free的区别
查看>>
一些杂七杂八
查看>>
界面实例--旋转的3d立方体
查看>>
Couldn't open file /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
查看>>
【习题2】3.机票预定系统--写出问题定义并分析此系统可行性
查看>>
spring配置文件中配置sessionFactory时,属性configLocations的作用(spring整合hibernate)...
查看>>
点击图表每一部分触发某事件
查看>>
用C/C++扩展你的PHP
查看>>
windows下面配置apache+http
查看>>
cocoapodscha有无插件时如何使用
查看>>
项目笔记
查看>>
SQL Server 2008 Database Mirroring
查看>>
nexus3.x启动不起来
查看>>
昆明凯杰
查看>>
4-12(2)
查看>>
sql序列(3)基本语法
查看>>