抛弃std::allocator以支持自定义解决方案的一些真正好的理由是什么?您是否遇到过这样的情况:它对于正确性、性能、可伸缩性等来说是绝对必要的?有什么聪明的例子吗?
自定义分配器一直是标准库的一个特性,但我并不太需要它。我只是想知道是否有人能提供一些令人信服的例子来证明他们的存在。
抛弃std::allocator以支持自定义解决方案的一些真正好的理由是什么?您是否遇到过这样的情况:它对于正确性、性能、可伸缩性等来说是绝对必要的?有什么聪明的例子吗?
自定义分配器一直是标准库的一个特性,但我并不太需要它。我只是想知道是否有人能提供一些令人信服的例子来证明他们的存在。
当前回答
强制性链接到Andrei Alexandrescu 2015年CppCon关于分配者的演讲:
https://www.youtube.com/watch?v=LIb3L4vKZ7U
好处是,只是设计它们让你想到如何使用它们:-)
其他回答
这里我使用的是自定义分配器;您甚至可以说它是为了绕过其他自定义动态内存管理。
背景:我们有malloc, calloc, free的重载,以及操作符new和delete的各种变体,并且链接器很高兴地让STL为我们使用这些。这让我们可以做一些事情,如自动小对象池,泄漏检测,分配填充,自由填充,填充分配与哨兵,缓存线对齐某些分配,和延迟释放。
问题是,我们正在一个嵌入式环境中运行——没有足够的内存来在一段较长的时间内正确地进行泄漏检测。至少,不是在标准RAM中——通过自定义分配函数,在其他地方还有另一堆RAM可用。
解决方案:编写一个使用扩展堆的自定义分配器,并且只在内存泄漏跟踪体系结构的内部使用它……其他所有内容默认为执行泄漏跟踪的普通新建/删除重载。这避免了跟踪器跟踪本身(并且提供了一些额外的打包功能,我们知道跟踪器节点的大小)。
出于同样的原因,我们也使用它来保存功能成本分析数据;为每个函数调用和返回编写一个条目,以及线程切换,成本会很快增加。自定义分配器再次在较大的调试内存区域中为我们提供较小的分配。
自定义分配器可以发挥作用的一个领域是游戏开发,特别是在游戏机上,因为它们只有少量内存,没有交换空间。在这样的系统上,您要确保对每个子系统都有严格的控制,这样一个不重要的系统就不能从一个重要的系统窃取内存。池分配器等其他功能可以帮助减少内存碎片。你可以在这里找到一篇关于这个主题的详细的长篇论文:
EASTL—Electronic Arts标准模板库
当使用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)
对于共享内存来说,不仅容器头存储在共享内存中,而且容器头包含的数据也存储在共享内存中,这一点至关重要。
Boost::Interprocess的分配器就是一个很好的例子。然而,正如你在这里读到的,这个allone是不够的,要使所有STL容器共享内存兼容(由于不同进程中的映射偏移量不同,指针可能会“中断”)。
我正在研究一个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。您可以在项目中使用它。