我正在尝试优化并发集合,尝试最小化读取的锁争用。第一遍是使用链表,这使我只能锁定写入,而许多同时读取可以继续解锁。这使用自定义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中进行测试以测试该理论。
答案 0 :(得分:4)
在每次迭代中获取和释放锁定听起来都是个坏主意 - 因为如果在迭代列表时执行Add
或Remove
,则会使迭代器无效。例如,List<T>
肯定不会那样。
您的用例是否允许调用者在整个迭代过程中取出ReaderWriterLockSlim
,而不是基于每个项目?那将更有效和更强大。如果没有,您打算如何处理并发问题?如果一个编写器添加一个元素早于我所到的位置,一个简单的实现将返回相同的元素两次。删除时会发生相反的情况 - 迭代器会跳过一个元素。
最后,.NET 4.0是一个选项吗?我知道那里有一些高度优化的并发集合......
编辑:我不太确定你手工构建迭代器的现状是什么,但你可能要调查的一件事就是使用IEnumerator<T>
的结构,并制作你的收藏明确声明它返回 - 这就是List<T>
的作用。 意味着使用一个可变结构,这使得小猫在世界各地都会哭泣,但如果这对性能绝对至关重要,并且你认为你可以忍受恐怖,那至少值得一试。< / p>