2010-01-28 21 views
9

我有一個小類(32位系統上的16個字節),我需要動態分配。在大多數情況下,任何給定實例的生命週期都很短。有些實例也可能跨線程邊界傳遞。高效地分配許多短命的小物體

做了一些分析後,我發現我的程序似乎花費了更多的時間來分配和釋放事物,而不是實際花費在使用它們,所以我想用更有效率的東西替換默認的new和delete。

對於一個大對象(數據庫連接發生的時候,這對於構造而不是分配來說是很昂貴的),我已經在使用一個池化系統,但是它包含一個存儲「空閒」對象的列表,還有一個互斥體爲線程安全。在互斥體和列表之間,它實際上比用於小對象的基本新/刪除更差。

我發現了一些谷歌上的小物件分配器的,但他們似乎可以用未在一個線程安全的方式使用的全局/靜態池,使它們不適合我使用:(

其他什麼我有選擇有效的內存管理這樣的小對象嗎?

+1

如果你的對象非常小,爲什麼不通過價值傳遞呢?另外,你的許多小物件是否一樣,或者它們都不同?如果第一個,看看boost.flyweight – KitsuneYMG 2010-01-28 18:19:24

+0

你的問題中沒有什麼證據表明你可以做得更好。你認爲你做了什麼? – 2010-01-28 18:29:18

+0

當你寫互斥體的時候,你的意思是一般還是互斥體呢?如果特別的話,只要你沒有跨越過程邊界,那麼CriticalSection會更好。 – 2010-01-28 18:41:42

回答

0

您可能確保您使用low fragmentation heap。它在Vista和更高版本默認情況下,但我認爲對於早期的操作系統是不是這樣。這對於小物件的分配速度有很大的影響。

+0

好吧,我正在測試Vista,所以在這種情況下,它已經開啓了。但值得一提的是,因爲我支持XP。 – 2010-01-28 18:24:31

+1

這是一個有趣的問題(更多,因爲我沒有解決它:)我很好奇看到最後的解決方案 – 2010-01-28 18:31:03

1

也許試試用usi Google的tcmalloc?它針對線程化程序中的快速分配/重新分配進行了優化,並且對於小型對象的開銷較低。

1

有些情況下也可以跨線程邊界

只有「一些」傳遞?所以也許你可以支付額外的費用,如果這些費用不會通過其他線程便宜的話。我可以通過各種方式來獲得每個線程的一個分配器,並避免在分配器所屬的線程中分配或釋放時需要鎖定。我不知道你的程序中可能有哪些可能:

  • 跨越線程邊界而不是傳遞它們。

  • 安排如果他們由於任何原因傳遞給另一個線程,然後他們被傳回原始線程以釋放。這不一定非要經常發生,你可以在接收線程中排隊幾個,稍後再將它們全部傳回。這當然假設擁有分配器的線程將繼續存在。

  • 每個分配器有兩個空閒列表,一個同步(當它們從另一個線程中釋放時添加到這些對象),另一個是非同步的。只有當非同步列表爲空,並且您正在分配(並因此在擁有分配器的線程中)時,是否需要鎖定同步的空閒列表並將其當前內容全部移到非同步列表中。如果傳遞給其他線程的對象很少,這基本上消除了互斥體的爭用並大大減少了它被佔用的次數。

  • 如果以上所有失敗,每個線程有一個分配器可能仍然允許您擺脫互斥鎖併爲空閒列表使用免鎖隊列(釋放多個寫入者,分配單個讀取器),這可以提高性能有點。實現無鎖隊列是特定於平臺的。

採取了一步回來,你的應用程序經常打的狀態下,你知道,所有細胞的某一點(也許在一點點過去)分配後,都不再使用?如果是這樣,並假設你的小物體的析構函數沒有做任何非常緊急的事情,那麼根本不要打擾釋放單元格 - 在「某個點」創建一個新的分配器並將舊分配器標記爲不再用於新分配。當你「觸及狀態」時,釋放整個分配器及其底層緩衝區。如果「確定點」和「狀態」是同時發生的,那就更簡單了 - 只需重新設置分配器即可。

+0

「在某個點(可能是過去的一段時間)之後,不再使用?」不幸的是,它們在程序的整個生命週期中都被使用了,接下來是每個線程的one-allocator。做了一個池分配器,它在某種類型的TLS中擁有自由列表,然後id只需要一個偶爾的同步過程來打擊對象從單線程移動到另一個線程的可能性,從而導致一個人釋放超過它分配的空間? – 2010-01-29 08:11:21

+0

是的,這聽起來似乎合理。從一個線程到另一個線程的漂移越慢,您不得不同步的次數越少,開銷越低。 – 2010-01-29 15:19:11

+0

你也可以看看這個:http://goog-perftools.sourceforge.net/doc/tcmalloc.html – 2010-01-31 17:46:24