2011-06-19 66 views
3

是否有任何可移植的方式來取代malloc()/ free()與類似STL的分配器的包裝的使用?使用分配器來替換malloc()/ free()?

上下文:我有一個C庫,允許指定用於內存管理的自定義malloc()/ free()函數,以及用於多線程上下文中的函數。尋找一個好的多線程分配器,我發現GCC-libstdC++的mt_alloc對我的工作負載表現非常好。現在我想在C庫中使用它,但怎麼做呢?

我看到的主要問題是在deallocate()函數中,與free()相反,除了地址以外,還分配了內存塊的大小。所以我需要以某種方式跟蹤與每個內存分配相關的大小,以便在釋放內存時將其反饋回deallocate()。我想要解決這個問題的最簡單的解決方案是將分配內存的大小存儲在內存塊的開始位置,但是我不確定如何解決可能出現的對齊問題。

有沒有簡單的解決方案,我忽略了?

+0

請記住,容器以越來越大的塊來分配內存,並且在容量減小時儲存它所擁有的內存。您的C庫可能不會有相同的使用模式,所以您甚至可能看不到與容器相同的性能改進。 –

+0

@Emile:我想要跟蹤的大小是分配額外的空間來存儲組塊大小_inin_大塊。因此,如果請求了n個字節,請分配類似n + sizeof(std :: size_t)(+ - 對齊注意事項)的內容,並返回基地址+ sizeof(std :: size_t)。釋放指針p時,取p - sizeof(std :: size_t),讀取大小並將其傳遞給deallocate()。 – bluescarni

+0

是的,當我讀到你的問題時,我不知何故錯過了。必須是ADD。 :-) –

回答

3

在我的平臺上,malloc可確保分配的內存在8字節邊界處對齊。爲了模仿這種行爲,使用allocator<uint64_t>

#include <stdint.h> 
#include <ext/mt_allocator.h> 

static __gnu_cxx::__mt_alloc<uint64_t> theAllocator; 

void* mtmalloc(size_t size) 
{ 
    // Divide size by sizeof(uint64_t) and round up 
    size_t payloadElementCount = (size + sizeof(uint64_t) - 1)/
           sizeof(uint64_t); 

    // Add an extra uint64_t to store the chunk size 
    size_t chunkElementCount = 1 + payloadElementCount; 

    // Allocate the chunk 
    uint64_t* chunk = theAllocator.allocate(chunkElementCount); 

    // Store the chunk size in the first word 
    chunk[0] = chunkElementCount; 

    // Return a pointer past where the chunk size is stored 
    return static_cast<void*>(chunk + 1); 
} 

void mtfree(void* pointer) 
{ 
    // The chunk begins one word before the passed in pointer 
    uint64_t* chunk = static_cast<uint64_t*>(pointer) - 1; 

    // Retrieve the chunk size 
    size_t chunkElementCount = chunk[0]; 

    // Deallocate the chunk 
    theAllocator.deallocate(chunk, chunkElementCount); 
} 

int main() 
{ 
    int* array = (int*)mtmalloc(sizeof(int) * 4); 
    array[0] = 0; 
    array[1] = 1; 
    array[2] = 2; 
    array[3] = 3; 
    mtfree(array); 
} 

您的平臺,替代uint64_t使用合適的類型。

你應該用Valgrind之類的東西來測試它,以確保沒有內存泄漏!


相反的uint64_t,你可以使用GCC的__BIGGEST_ALIGNMENT__和Boost的aligned_storagetype trait的解決方案移植到GCC編譯器:

typedef boost::aligned_storage<__BIGGEST_ALIGNMENT__, __BIGGEST_ALIGNMENT__> AlignedType;

+0

不會你的size_t chunkSize = 1 + payloadSize;將指針大小增加1個字節,同時對uint64_t *進行指針轉換,然後減1,實際上減小指針sizeof(uint64_t)?這基本上意味着當有人試圖分配X字節時,實際上只是分配X-(sizeof(uint64_t)-1)個字節並返回這樣一個指針? – Simon

+1

@Simon:'allocator :: allocate'將**元素**的數目作爲參數,而不是以字節爲單位的大小。見http://cplusplus.com/reference/std/memory/allocator/allocate/ –

+0

啊,我看到:)我的壞,+1! – Simon

0

my answer here關於存儲在塊的開頭的值。您可以稍微修改它以滿足您的需求。

0

我知道的對象大小跟蹤的兩種主要方法隱含在大小分離的分配器中,其中的元數據位於旁邊(例如Kingsley樣式的分配器),或者將對象前面的大小定爲一個對象頭文件(例如dlmalloc)。一個非常糟糕的第三個解決方案是維護每個分配對象的地圖及其大小。那張地圖當然會由另一個分配器來管理。

我認爲你是在正確的軌道上,並且你很瞭解對齊方面的考慮因素。我試圖查找有關mt_alloc的一些信息,以查看是否有其他選擇或意外,但這些信息似乎並不容易。一些分配器有一個方法來查詢對象的大小(這可能或不便宜)。如果deallocate函數需要顯式地傳遞大小,那麼我猜想沒有這樣的函數存在,但你永遠不知道。

如果對齊很重要,你的計算需要稍微調整一下,因爲分配器可能不會返回適合你的內存。如果你一無所知返回指針的定位,你需要的東西,如:

struct object_header { 
    size_t size; 
}; 

void * buf = xxmalloc (2 * alignment + size + sizeof(object_header)); 
void * alignedPtr = (void *) (((size_t) buf + sizeof(object_header) + alignment - 1) & ~(alignment - 1)); 

如果mt_alloc無法容忍內部指針釋放的對象,那麼這個計劃,因爲通過填充出來對準額外的空間提出你的問題,你不再知道返回給你的原始地址。在這種情況下,您可能需要在標題中存儲額外的字段。

根據mt_alloc在內部管理內存的方式,添加額外的頭文件也會給您帶來很大的開銷。在大小獨立的分配器中,加上這個頭文件可以使您在達到頁面大小的對象上最多支持2倍的空間開銷,在這一點上,您可以支付每個對象額外頁面的成本。在其他分配器中,這可能不是問題,但需要注意。

+0

從什麼時候開始malloc的8字節對齊標準?該標準規定:「如果分配成功,返回的指針適當地對齊,以便它可以被分配給指向任何類型對象的任何類型的指針 ,然後用於在分配的空間中訪問這樣的對象或這樣的對象的數組 (直到明確釋放空間爲 )。「,這顯然是平臺(和編譯器)特定的。 – Simon

+0

你說得對,我一直在GNU的土地上工作太久。事實上,我應該知道更好,因爲我們一直在分配器周圍包裝層來執行此操作。 –