抛弃std::allocator以支持自定义解决方案的一些真正好的理由是什么?您是否遇到过这样的情况:它对于正确性、性能、可伸缩性等来说是绝对必要的?有什么聪明的例子吗?
自定义分配器一直是标准库的一个特性,但我并不太需要它。我只是想知道是否有人能提供一些令人信服的例子来证明他们的存在。
抛弃std::allocator以支持自定义解决方案的一些真正好的理由是什么?您是否遇到过这样的情况:它对于正确性、性能、可伸缩性等来说是绝对必要的?有什么聪明的例子吗?
自定义分配器一直是标准库的一个特性,但我并不太需要它。我只是想知道是否有人能提供一些令人信服的例子来证明他们的存在。
当前回答
这里我使用的是自定义分配器;您甚至可以说它是为了绕过其他自定义动态内存管理。
背景:我们有malloc, calloc, free的重载,以及操作符new和delete的各种变体,并且链接器很高兴地让STL为我们使用这些。这让我们可以做一些事情,如自动小对象池,泄漏检测,分配填充,自由填充,填充分配与哨兵,缓存线对齐某些分配,和延迟释放。
问题是,我们正在一个嵌入式环境中运行——没有足够的内存来在一段较长的时间内正确地进行泄漏检测。至少,不是在标准RAM中——通过自定义分配函数,在其他地方还有另一堆RAM可用。
解决方案:编写一个使用扩展堆的自定义分配器,并且只在内存泄漏跟踪体系结构的内部使用它……其他所有内容默认为执行泄漏跟踪的普通新建/删除重载。这避免了跟踪器跟踪本身(并且提供了一些额外的打包功能,我们知道跟踪器节点的大小)。
出于同样的原因,我们也使用它来保存功能成本分析数据;为每个函数调用和返回编写一个条目,以及线程切换,成本会很快增加。自定义分配器再次在较大的调试内存区域中为我们提供较小的分配。
其他回答
当使用gpu或其他协处理器时,以特殊的方式在主存中分配数据结构有时是有益的。这种特殊的内存分配方式可以在自定义分配器中以一种方便的方式实现。
在使用加速器时,通过加速器运行时进行自定义分配是有益的,原因如下:
through custom allocation the accelerator runtime or driver is notified of the memory block in addition the operating system can make sure that the allocated block of memory is page-locked (some call this pinned memory), that is, the virtual memory subsystem of the operating system may not move or remove the page within or from memory if 1. and 2. hold and a data transfer between a page-locked memory block and an accelerator is requested, the runtime can directly access the data in main memory since it knows where it is and it can be sure the operating system did not move/remove it this saves one memory copy that would occur with memory that was allocated in a non-page-locked way: the data has to be copied in main memory to a page-locked staging area from with the accelerator can initialize the data transfer (through DMA)
自定义分配器是在释放内存之前安全地擦除内存的合理方法。
template <class T>
class allocator
{
public:
using value_type = T;
allocator() noexcept {}
template <class U> allocator(allocator<U> const&) noexcept {}
value_type* // Use pointer if pointer is not a value_type*
allocate(std::size_t n)
{
return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
}
void
deallocate(value_type* p, std::size_t) noexcept // Use pointer if pointer is not a value_type*
{
OPENSSL_cleanse(p, n);
::operator delete(p);
}
};
template <class T, class U>
bool
operator==(allocator<T> const&, allocator<U> const&) noexcept
{
return true;
}
template <class T, class U>
bool
operator!=(allocator<T> const& x, allocator<U> const& y) noexcept
{
return !(x == y);
}
推荐使用Hinnant的allocator样板: https://howardhinnant.github.io/allocator_boilerplate.html)
我正在研究一个mmap-分配器,它允许向量使用内存 内存映射文件。我们的目标是让向量使用这样的存储 直接在由mmap映射的虚拟内存中。我们的问题是 提高真正大的文件(>10GB)的读取到内存,而不复制 开销,因此我需要这个自定义分配器。
到目前为止,我已经有了一个自定义分配器的骨架 (它来源于std::allocator),我认为这是一个很好的开始 指向写自己的分配器。请随意使用这段代码 以任何你想要的方式:
#include <memory>
#include <stdio.h>
namespace mmap_allocator_namespace
{
// See StackOverflow replies to this answer for important commentary about inheriting from std::allocator before replicating this code.
template <typename T>
class mmap_allocator: public std::allocator<T>
{
public:
typedef size_t size_type;
typedef T* pointer;
typedef const T* const_pointer;
template<typename _Tp1>
struct rebind
{
typedef mmap_allocator<_Tp1> other;
};
pointer allocate(size_type n, const void *hint=0)
{
fprintf(stderr, "Alloc %d bytes.\n", n*sizeof(T));
return std::allocator<T>::allocate(n, hint);
}
void deallocate(pointer p, size_type n)
{
fprintf(stderr, "Dealloc %d bytes (%p).\n", n*sizeof(T), p);
return std::allocator<T>::deallocate(p, n);
}
mmap_allocator() throw(): std::allocator<T>() { fprintf(stderr, "Hello allocator!\n"); }
mmap_allocator(const mmap_allocator &a) throw(): std::allocator<T>(a) { }
template <class U>
mmap_allocator(const mmap_allocator<U> &a) throw(): std::allocator<T>(a) { }
~mmap_allocator() throw() { }
};
}
为了使用它,像下面这样声明一个STL容器:
using namespace std;
using namespace mmap_allocator_namespace;
vector<int, mmap_allocator<int> > int_vec(1024, 0, mmap_allocator<int>());
例如,每当分配内存时,就可以使用它记录日志。什么是必要的 是重新绑定结构,否则向量容器使用超类分配/释放 方法。
更新:内存映射分配器现在可以在https://github.com/johannesthoma/mmap_allocator上获得,并且是LGPL。您可以在项目中使用它。
前一段时间我发现这个解决方案对我非常有用:STL容器的快速c++ 11分配器。它略微加快了VS2017上的STL容器(~5倍)以及GCC上的STL容器(~7倍)。它是一种基于内存池的特殊用途的分配器。它可以与STL容器一起使用,这多亏了您所要求的机制。
在图形模拟中,我看到自定义分配器用于
std::allocator不直接支持的对齐约束。 通过为短期分配(只是这个框架)和长期分配使用单独的池来最小化碎片。