[英]Using allocators to replace malloc()/free()?
是否有任何可移植的方法來替換類似STL的分配器的包裝器來替換malloc()/ free()的使用?
上下文:我有一個C庫,允許指定自定義的malloc()/ free() - 類似於內存管理的函數,並且在多線程上下文中使用。 為了找到一個好的多線程分配器,我發現GCC-libstdc ++的mt_alloc對我的工作負載表現非常好。 現在我想在所說的C庫中使用它,但是怎么做呢?
我看到的主要問題是deallocate()函數,與free()相反,除了它的地址之外,還取得了分配的內存塊的大小。 因此,我需要以某種方式跟蹤與每個內存分配相關聯的大小,以便在釋放內存時可以將其反饋給deallocate()。 我想要解決的最簡單的解決方案是在內存塊的開頭存儲已分配內存的大小,但后來我不確定如何解決可能出現的對齊問題。
我有什么簡單的解決方案嗎?
在我的平台上, 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這樣的東西進行測試,以確保沒有內存泄漏!
您可以使用GCC的__BIGGEST_ALIGNMENT__
和Boost的aligned_storage
類型特征代替uint64_t
,以獲得可移植到GCC編譯器的解決方案:
typedef boost::aligned_storage<__BIGGEST_ALIGNMENT__, __BIGGEST_ALIGNMENT__> AlignedType;
保羅·拉斯卡(Paul Laska)在altdevblogaday上寫了一篇很棒的系列文章。 這是第一篇文章的鏈接: http : //altdevblogaday.org/2011/04/11/ready-set-allocate-part-1/
在文章中,他關注塊大小分配和對齊問題。 它應該提供一個經過深思熟慮並且編寫良好的解決方案來處理您的解除分配問題。
請參閱我的答案,關於在塊的開頭存儲值。 您可以根據需要稍微修改它。
我所知道的兩種主要的對象大小跟蹤方法隱含在一個大小隔離的分配器中,元數據一邊向外(例如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倍的空間開銷,此時您可以支付每個對象額外頁面的成本。 在其他分配器中,這可能不是問題,但需要注意的事項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.