繁体   English   中英

使用allocator来替换malloc()/ free()?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM