Doa*_*ong 7 c# ienumerable list
今天,我在迭代一系列项目时遇到了性能问题.在完成一些诊断之后,我终于找到了降低性能的原因.事实证明,迭代IEnumerable<T>一次比花费更多的时间List<T>.请帮我理解为什么IEnumerable<T>比慢List<T>.
UPDATE基准上下文:
我正在使用NHibernate从数据库中获取项目集合IEnumerable<T>并对其属性值进行求和.这只是一个没有任何引用类型的简单实体:
public SimpleEntity
{
public int Id {get;set}
public string Name {get;set}
public decimal Price {get;set}
}
Public Test
{
void Main()
{
//this query get a list of about 200 items
IEnumerable<SimpleEntity> entities = from entity in Session.Query<SimpleEntity>
select entity;
decimal value = 0.0;
foreach(SimpleEntity item in entities)
{
//this for loop took 1.5 seconds
value += item.Price;
}
List<SimpleEntity> lstEntities = entities.ToList();
foreach(SimpleEntity item in lstEntities)
{
//this for loop took less than a milisecond
value += item.Price;
}
}
}
Run Code Online (Sandbox Code Playgroud)
List<T> 是一个IEnumerable<T>.当您在迭代时List<T>,您正在执行与其他任何操作相同的操作序列IEnumerable<T>:
IEnumerator<T>.IEnumerator<T>.MoveNext()您的枚举器.IEnumerator<T>.Current从IEnumerator接口获取元素.MoveNext()trueIEnumerator<T>.我们所知道的List<T>是它是一个内存中的集合,因此MoveNext()它的枚举器上的函数将非常便宜.看起来您的集合提供了一个枚举器,其MoveNext()方法更昂贵,可能是因为它与某些外部资源(如数据库连接)进行交互.
当您调用ToList()时IEnumerable<T>,您正在运行集合的完整迭代,并使用该迭代将所有元素加载到内存中.如果您希望多次迭代同一个集合,这是值得的.如果您希望只迭代集合一次,那么这ToList()是一种虚假的经济:它所做的只是创建一个内存集合,以后必须进行垃圾收集.
枚举IEnumerable<T>比List<T>直接枚举慢2到3倍。这是由于C#如何为给定类型选择其枚举器而引起的。
List<T> 公开了3个枚举器:
List<T>.Enumerator List<T>.GetEnumerator()IEnumerator<T> IEnumerable<T>.GetEnumerator()IEnumerator IEnumerable.GetEnumerator()C#编译foreach循环时,它将按上述顺序选择枚举数。请注意,一种类型不需要实现IEnumerable或IEnumerable<T>可枚举,它只需要一种名为的方法即可GetEnumerator()返回枚举数。
现在,List<T>.GetEnumerator()有被静态类型,这使得所有的呼叫优势List<T>.Enumerator.get_Current和List<T>.Enumerator.MoveNext()静态绑定,而不是虚拟的。
10M次迭代(coreclr):
for(int i ...) 73 ms
foreach(... List<T>) 215 ms
foreach(... IEnumerable<T>) 698 ms
foreach(... IEnumerable) 1028 ms
for(int *p ...) 50 ms
Run Code Online (Sandbox Code Playgroud)
10M次迭代(框架):
for(int i ...) 210 ms
foreach(... List<T>) 252 ms
foreach(... IEnumerable<T>) 537 ms
foreach(... IEnumerable) 844 ms
for(int *p ...) 202 ms
Run Code Online (Sandbox Code Playgroud)
免责声明
我应该指出,列表中的实际迭代很少会成为瓶颈。请记住,这是数百万次迭代中的数百毫秒。循环中比一些算术运算更复杂的任何工作都将比迭代本身更加昂贵。