我经常遇到这样的情况:我想在声明查询的地方对查询进行求值。这通常是因为我需要对它进行多次迭代,计算成本很高。例如:
string raw = "...";
var lines = (from l in raw.Split('\n')
let ll = l.Trim()
where !string.IsNullOrEmpty(ll)
select ll).ToList();
这很好。但是如果我不打算修改结果,那么我也可以调用ToArray()而不是ToList()。
然而,我想知道ToArray()是否通过首先调用ToList()来实现,因此内存效率比只调用ToList()低。
我疯了吗?我是否应该调用ToArray() -在知道内存不会被分配两次的情况下安全可靠?
一个很晚的答案,但我认为这对谷歌人有帮助。
They both suck when they created using linq. They both implement same code to resize buffer if necessary. ToArray internally uses a class to convert IEnumerable<> to array, by allocating an array of 4 elements. If that is not enough than it doubles the size by creating a new array double the size of current and copying current array to it. At the end it allocates a new array of count of your items. If your query returns 129 elements then ToArray will make 6 allocations and memory copy operations to create a 256 element array and than am another array of 129 to return. so much for memory efficiency.
ToList做同样的事情,但是它跳过了最后的分配,因为您可以在将来添加项。List不关心它是从linq查询创建的还是手动创建的。
List在内存上更好,但在cpu上更差,因为List是一个通用的解决方案,每个操作都需要范围检查,除了.net内部的数组范围检查之外。
因此,如果你将迭代你的结果集太多次,那么数组是很好的,因为它意味着比列表更少的范围检查,编译器通常优化数组的顺序访问。
如果在创建List时指定capacity参数,则它的初始化分配可以更好。在这种情况下,它将只分配数组一次,假设您知道结果大小。linq的ToList没有指定重载来提供它,因此我们必须创建扩展方法,该方法创建一个具有给定容量的列表,然后使用list <>. addrange。
为了完成这个问题,我必须写出下面的句子
At the end, you can use either an ToArray, or ToList, performance will not be so different ( see answer of @EMP ).
You are using C#. If you need performance then do not worry about writing about high performance code, but worry about not writing bad performance code.
Always target x64 for high performance code. AFAIK, x64 JIT is based on C++ compiler, and does some funny things like tail recursion optimizations.
With 4.5 you can also enjoy the profile guided optimization and multi core JIT.
At last, you can use async/await pattern to process it quicker.