2009-11-18 83 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