什么减慢了简单的集合转换

时间:2012-05-28 10:48:50

标签: c# arrays collections foreach ienumerable

我有一个简单的while循环

IEnumerable<Foo> collection;
while (!bc.IsCompleted)
{
   collection = bc.Take();
}

bc是BlockingCollection<IEnumerable<Foo>>。 bc包含9个IEnumerable集合和总共260万个Foo对象。循环大约需要640毫秒才能在我的机器上运行。只要我在while循环中的Take()之后添加一个foreach循环,就可以将爆炸运行到2400ms。

foreach(Foo foo in collection)
{
}

迭代列表中的260万个元素或者我单独设置的Foo []或IEnumerable,耗时约54ms。

如果不是foreach循环,我只需添加一个集合转换,例如

,就会发生同样的情况
List<Foo> fooList = collection.ToList();

Foo[] fooArray = collection.ToArray();

它突然也需要2000ms的北部才能执行。

这怎么可能?我完全没有解释或可能的原因。谁能指出我在这里失踪的人?缓慢不能由锁定/阻塞引起,因为我没有改变在比较之间访问BlockingCollection的方式。

感谢您的任何意见。

3 个答案:

答案 0 :(得分:1)

你在队列中放入了哪种IEnumerable

请记住,LINQ查询正在使用延迟执行;您的代码最终可能会在使用者线程上评估查询。在将元素放入队列之前,尝试在生产者线程上调用ToList()

答案 1 :(得分:1)

IEnumerable可以表示延迟操作。有时(例如,使用LINQ或迭代器块),实际上不会生成ienumerable的内容,直到它被迭代。

因此,您的IEnumerable<Foo>可能包含足够的信息来生成Foo,但实际上并没有这样做,直到您在foreach或{{1}中迭代可枚举为止}}。这就是为什么这些操作需要很长时间。

答案 2 :(得分:1)

与其他LINQ方法一样(并且我猜你正在使用LINQ),这种方法适用于一个自定义执行:

  

此方法通过使用延迟执行来实现。立即返回值是一个对象,它存储执行操作所需的所有信息。直到通过直接调用其GetEnumerator方法或在Visual C#中使用foreach或在Visual Basic中使用for Each来枚举对象时,才会执行此方法表示的查询

这意味着如果你不添加foreach循环的ToList()调用,对Take的唯一调用并不会产生任何结果,并且只有在使用迭代器(foreach / tolist)时才会产生实际结果。因此性能差异。

您只是迭代列表的比较可能无法提供准确的结果;它不是需要时间的List(foo)上的迭代,它可能是你正在使用的阻塞集合中的元素的选择,这会减慢一切。
 MSDN声称在BlockingCollection上使用常规foreach(这可能是你使用LINQ提供的Take,在这种情况下在IEunmerable上工作时会发生的事情)使用底层集合的snashop,这肯定会减慢处理大量藏品。