2009-11-18 95 views
4

我想优化一个并发集合,试图最小化读取的锁争用。第一遍使用的是链接列表,这允许我只锁定写入,而许多同时读取可以继续畅通。这使用一个自定义的IEnumerator产量下一个链接值。一旦我开始比较了集合迭代纯List<T>我发现我实现一半左右的速度(对于from x in c select x上的1 * M *项目的集合,我得到24MSList<T>49ms我的集合)。寻找IEnumerable/IEnumerator更快的实现

所以我想我会使用ReaderWriteLockSlim并牺牲读取的一点争论,所以我可以使用List<T>作为我的内部存储。由于我必须在迭代开始时捕获读锁,并在完成时释放读锁,因此我首先对我的IEnumerableforeach进行了内部List<T>的良率模式。现在我只得到66ms

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

然而,即使模仿IEnumerator移动索引的阵列,我能做的最好的是大约~38ms。那么,究竟是什么给了它的秘诀,或者迭代器的更快实现呢?

更新:原来我的主要速度罪魁祸首是运行调试编译,而List<T>显然是一个发布编译。在发布中,我的实现仍然比List<T>慢,尽管单声道现在更快。

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

+0

@Arne:我添加了一个你可能想尝试的编辑。事实上,如果你不需要检测并发修改,你应该能够*击败'List '的性能:) – 2009-11-18 07:36:34

回答

4

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

您的使用案例是否允许调用者围绕其整个迭代过程取出ReaderWriterLockSlim,而不是基于每个项目?那会更有效率更健壮。如果不是,你打算如何处理并发问题?如果一个作者比我需要的地方更早地添加一个元素,一个简单的实现会返回两次相同的元素。相反的情况会在删除时发生 - 迭代器会跳过一个元素。

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

编辑:我不太清楚你目前的情况是在手动构建一个迭代器方面,但有一件事你可能想要调查使用一个IEnumerator<T>的结构,并使你的集合显式地声明它返回那个 - 这就是List<T>所做的。它确实意味着使用可变的结构,这使得小猫哭泣世界各地,但如果这是对性能绝对至关重要,你认为你可以忍受恐怖,至少值得一试。

+0

Jon - 我更新了我的帖子,以澄清我在迭代开始时获取锁并释放它一旦迭代完成。收集的重点正是修改会使迭代器失效的问题,因此我的第一个读者无锁实现,以及我目前对ReaderWriterLockSlim的考虑,以允许许多具有廉价锁定语义的并发读取器。 .NET 4.0不是一个选项,因为这个代码需要在单声道上运行。 – 2009-11-18 06:55:33

+1

来自.NET 4的高性能集合被Reactive Extensions团队移植到.NET 3.5。 RX Team团队博客中的“System.Threading,.NET 4并发扩展到.NET 3.5 SP1的反向链接”:http://blogs.msdn.com/rxteam/archive/2009/11/17/release-notes.aspx – 2009-11-18 07:01:29

+0

哦,是的,就像6个小时前一样。 – 2009-11-18 07:02:25