Naa*_*aff 166 c++ memory-management std memory-alignment allocator
有什么理由放弃std::allocator
支持自定义解决方案?您是否遇到过正确性,性能,可扩展性等绝对必要的情况?有什么非常聪明的例子吗?
自定义分配器一直是我不太需要的标准库的一个功能.我只是想知道SO上的任何人是否可以提供一些令人信服的例子来证明他们的存在.
tim*_*day 113
正如我在这里提到的,我已经看到英特尔TBB的自定义STL分配器只需更改单个版本即可显着提高多线程应用程序的性能
std::vector<T>
Run Code Online (Sandbox Code Playgroud)
至
std::vector<T,tbb::scalable_allocator<T> >
Run Code Online (Sandbox Code Playgroud)
(这是切换分配器以使用TBB的漂亮线程私有堆的快速方便的方法;请参阅本文档的第7页)
Gru*_*bel 79
自定义分配器可用的一个领域是游戏开发,特别是在游戏控制台上,因为它们只有少量内存而且没有交换.在这样的系统上,您需要确保对每个子系统进行严格控制,这样一个不加批判的系统就无法从关键系统中窃取内存.池分配器等其他功能可以帮助减少内存碎片.您可以在以下位置找到有关该主题的详细论文:
Joh*_*oma 61
我正在研究一个mmap-allocator,它允许向量使用内存映射文件中的内存.目标是使用直接在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() { }
};
}
Run Code Online (Sandbox Code Playgroud)
要使用它,请按如下方式声明STL容器:
using namespace std;
using namespace mmap_allocator_namespace;
vector<int, mmap_allocator<int> > int_vec(1024, 0, mmap_allocator<int>());
Run Code Online (Sandbox Code Playgroud)
例如,它可以用于在分配内存时记录.必要的是重新绑定结构,否则向量容器使用超类allocate/deallocate方法.
更新:内存映射分配器现在可在https://github.com/johannesthoma/mmap_allocator上获得,并且是LGPL.随意将它用于您的项目.
Tho*_*Low 25
我正在使用一个使用c ++代码的MySQL存储引擎.我们使用自定义分配器来使用MySQL内存系统,而不是与MySQL竞争内存.它允许我们确保我们使用内存作为用户配置MySQL使用,而不是"额外".
Mar*_*ote 18
使用自定义分配器来使用内存池而不是堆可能很有用.这是许多其他人中的一个例子.
对于大多数情况,这肯定是一个不成熟的优化.但它在某些情况下(嵌入式设备,游戏等)非常有用.
我没有使用自定义STL分配器编写C++代码,但我可以想象一个用C++编写的Web服务器,它使用自定义分配器自动删除响应HTTP请求所需的临时数据.一旦生成响应,自定义分配器可以立即释放所有临时数据.
自定义分配器(我使用过)的另一个可能用例是编写单元测试来证明函数的行为不依赖于其输入的某些部分.自定义分配器可以用任何模式填充内存区域.
小智 7
自定义分配器是在释放内存之前安全擦除内存的合理方法。
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);
}
Run Code Online (Sandbox Code Playgroud)
推荐使用 Hinnant 的分配器样板: https://howardhinnant.github.io/allocator_boilerplate.html)
我正在使用自定义分配器来计算我程序的一部分中的分配/解除分配数量并测量需要多长时间。还有其他方法可以实现,但这种方法对我来说非常方便。我可以将自定义分配器仅用于我的容器的一个子集,这一点特别有用。
小智 6
一种基本情况:在编写必须跨模块 (EXE/DLL) 边界工作的代码时,必须确保分配和删除只发生在一个模块中。
我遇到的地方是 Windows 上的插件架构。例如,如果您跨 DLL 边界传递 std::string,则该字符串的任何重新分配都发生在它起源的堆中,而不是 DLL 中可能不同的堆中,这一点很重要*。
*实际上比这更复杂,就好像您正在动态链接到 CRT 一样,这可能无论如何都可以工作。但是,如果每个 DLL 都有一个指向 CRT 的静态链接,那么您将进入一个痛苦的世界,幻象分配错误不断发生。
使用GPU或其他协处理器时,有时以特殊方式在主存储器中分配数据结构是有益的.这种分配内存的特殊方式可以方便的方式在自定义分配器中实现.
使用加速器时通过加速器运行时自定义分配的原因如下:
我在这里使用自定义分配器; 你甚至可能会说这是为了解决其他自定义动态内存管理问题.
背景:我们有malloc,calloc,free以及operator new和delete的各种变体的重载,并且链接器很高兴让STL为我们使用这些.这让我们可以做一些事情,比如自动小对象池,泄漏检测,分配填充,免费填充,带有哨兵的填充分配,某些分配的缓存行对齐以及延迟免费.
问题是,我们正在嵌入式环境中运行 - 没有足够的内存来实际在长时间内正确地进行泄漏检测.至少,不是在标准RAM中 - 通过自定义分配功能,在其他地方可以使用另一堆RAM.
解决方案:编写一个使用扩展堆的自定义分配器,并仅在内存泄漏跟踪体系结构的内部使用它...其他所有内容都默认为执行泄漏跟踪的正常新/删除重载.这样可以避免跟踪器跟踪本身(并提供一些额外的打包功能,我们知道跟踪器节点的大小).
出于同样的原因,我们还使用它来保存功能成本分析数据; 为每个函数调用和返回写入一个条目,以及线程切换,可以快速获得成本.自定义分配器再次在较大的调试内存区域中为我们提供较小的分配.
我使用这些的一个例子是处理资源非常有限的嵌入式系统。假设您有 2k 空闲内存,并且您的程序必须使用其中的一些内存。您需要将 4-5 个序列存储在不在堆栈上的某个位置,此外您还需要非常精确地访问这些内容的存储位置,在这种情况下您可能需要编写自己的分配器。默认实现可能会产生内存碎片,如果您没有足够的内存并且无法重新启动程序,这可能是不可接受的。
我正在进行的一个项目是在一些低功耗芯片上使用 AVR-GCC。我们必须存储 8 个长度可变但最大值已知的序列。内存管理的标准库实现是一个围绕 malloc/free 的薄包装器,它通过在每个分配的内存块前面添加一个指向该分配的内存块末尾的指针来跟踪放置项目的位置。当分配新的内存块时,标准分配器必须遍历每个内存块,以找到适合所请求的内存大小的下一个可用块。在桌面平台上,这对于这几个项目来说非常快,但您必须记住,相比之下,其中一些微控制器非常慢且原始。此外,内存碎片问题是一个大问题,这意味着我们别无选择,只能采取不同的方法。
所以我们所做的就是实现我们自己的内存池。每个内存块都足够大,可以容纳我们需要的最大序列。这会提前分配固定大小的内存块并标记当前正在使用哪些内存块。我们通过保留一个 8 位整数来做到这一点,其中每一位代表是否使用了某个块。我们在这里权衡了内存使用量,试图使整个过程更快,在我们的例子中,这是合理的,因为我们正在推动这个微控制器芯片接近其最大处理能力。
还有很多时候,我可以看到在嵌入式系统的上下文中编写自己的自定义分配器,例如,如果序列的内存不在主内存中,而这些平台上经常出现这种情况。
Andrei Alexandrescu 在 CppCon 2015 上关于分配器的演讲的强制性链接:
https://www.youtube.com/watch?v=LIb3L4vKZ7U
好消息是,只要设计它们,你就会想到如何使用它们的想法:-)