C#: ToArray性能
C#: ToArray性能
背景:
我承认我没有尝试对此进行基准测试,但我很好奇...
Enumerable.ToArray
(以及它的兄弟方法Enumerable.ToList
)的CPU/内存特性如何?
由于IEnumerable
无法预先告知其元素数量,我(可能天真地)假设ToArray
会“猜测”一个初始数组大小,如果第一次猜测的大小太小,则重新调整/重新分配数组大小,然后如果第二次猜测的大小仍然太小,则再次调整大小等... 这将导致性能不如线性增长。
我可以想象更好的方法,其中涉及(混合)列表,但这仍然需要多次分配(虽然不需要重新分配)和相当多的复制,尽管在开销方面可能是线性的。
问题:
背后是否有任何“魔法”避免了这种重复调整大小的需求,使得ToArray
的空间和时间复杂度呈线性关系?
更一般地说,是否有关于BCL性能特性的“官方”文档?
在C#中,使用.ToArray()方法将集合转换为数组可能会导致性能问题。这个问题的出现的原因是,ToArray()方法在内部使用了一种称为“倍增算法”的技术。这意味着在转换过程中,数组的大小将被多次倍增,这可能会导致额外的内存分配和复制操作,从而影响性能。
需要注意的是,对于大多数类型来说,转换为数组只需要存储引用即可,不需要分配足够的内存来复制整个对象(除非你使用了大量的结构体)。因此,尽量避免在不必要的情况下使用.ToArray()或.ToList()方法。大多数情况下,你可以一直使用IEnumerable
为了解决这个性能问题,我们可以采用以下方法:
- 尽量延迟使用.ToArray()或.ToList()方法,直到确实需要将集合转换为数组或列表。
- 如果可能的话,尽量使用IEnumerable
- 如果必须使用.ToArray()或.ToList()方法,可以考虑在转换操作之前先估计集合的大小,并分配足够的内存空间。这样可以避免多次倍增操作,提高性能。
下面是一个示例代码,演示了如何使用估计大小的方法来优化.ToArray()方法的性能:
var collection = GetCollection(); // 获取集合 int estimatedSize = collection.Count(); // 估计集合的大小 var array = new T[estimatedSize]; // 分配足够大小的数组 int index = 0; foreach (var item in collection) { array[index++] = item; } // 现在可以使用转换后的数组进行后续操作
通过以上方法,我们可以避免不必要的内存分配和复制操作,提高.ToArray()方法的性能。总之,尽量避免不必要的集合转换操作,可以在性能上获得一定的优化。
问题的出现原因:
问题的出现是因为在执行C#中的ToArray方法时,如果被操作的IEnumerable
解决方法:
为了避免性能下降,可以尽量避免调用ToArray方法。在需要使用查询结果时,直接对查询进行操作通常是更好的选择。这样可以避免数组的创建和调整大小的操作,提高性能。
以下是整理后的文章:
在C#中,当我们需要将IEnumerable
ToArray方法的实现并没有什么神奇之处,如果需要,它会根据需要进行数组大小的调整。需要注意的是,并不总是需要调整数组大小的。如果被操作的IEnumerable
foreach (TElement current in source) { if (array == null) { array = new TElement[4]; } else { if (array.Length == num) { // Doubling happens *here* TElement[] array2 = new TElement[checked(num * 2)]; Array.Copy(array, 0, array2, 0, num); array = array2; } } array[num] = current; num++; }
需要注意的是,当数组填满时会发生大小加倍的操作。
无论如何,一般来说,除非绝对需要,我们应该尽量避免调用ToArray和ToList方法。在需要使用查询结果时,直接对查询进行操作通常是更好的选择。这样可以避免数组的创建和调整大小的操作,提高性能。