寻找更快的IEnumerable / IEnumerator实现

时间:2009-11-18 06:04:20

标签: c# performance ienumerable yield ienumerator

我正在尝试优化并发集合,尝试最小化读取的锁争用。第一遍是使用链表,这使我只能锁定写入,而许多同时读取可以继续解锁。这使用自定义IEnumerator 产生下一个链接值。一旦我开始将集合上的迭代与普通List<T>进行比较,我注意到我的实现大约快了一半(对于1 * m *项目的集合上的from x in c select x,我得到 24ms > List<T> 49ms

所以我认为我会使用ReaderWriteLockSlim并在阅读上牺牲一点争用,所以我可以使用List<T>作为我的内部存储空间。因为我必须在迭代开始时捕获读锁定并在完成时释放它,所以我首先对我的IEnumerable foreach 对内部List<T>执行了一个收益模式。现在我只有 66ms

我偷看了List实际上做了什么,它使用了T[]的内部存储和一个自定义IEnumerator来向前移动索引并返回当前索引值。现在,手动使用T[]作为存储意味着需要更多维护工作,但是,我正在追逐微秒。

然而,即使模仿IEnumerator移动数组上的索引,我能做的最好的还是 ~38ms 。那么是什么赋予了List<T>它的秘密含量,或者什么是迭代器更快的实现呢?

UPDATE:结果我的主要速度罪魁祸首是运行Debug编译,而List<T>显然是Release编译。在发布中,我的实现仍然比List<T>更慢,而单声道它现在更快。

我从朋友那里得到的另一个建议是BCL更快,因为它在GAC中,因此可以由系统预编译。将不得不在GAC中进行测试以测试该理论。

1 个答案:

答案 0 :(得分:4)

在每次迭代中获取和释放锁定听起来都是个坏主意 - 因为如果在迭代列表时执行AddRemove,则会使迭代器无效。例如,List<T>肯定不会那样。

您的用例是否允许调用者在整个迭代过程中取出ReaderWriterLockSlim,而不是基于每个项目?那将更有效更强大。如果没有,您打算如何处理并发问题?如果一个编写器添加一个元素早于我所到的位置,一个简单的实现将返回相同的元素两次。删除时会发生相反的情况 - 迭代器会跳过一个元素。

最后,.NET 4.0是一个选项吗?我知道那里有一些高度优化的并发集合......

编辑:我不太确定你手工构建迭代器的现状是什么,但你可能要调查的一件事就是使用IEnumerator<T>的结构,并制作你的收藏明确声明它返回 - 这就是List<T>的作用。 意味着使用一个可变结构,这使得小猫在世界各地都会哭泣,但如果这对性能绝对至关重要,并且你认为你可以忍受恐怖,那至少值得一试。< / p>