引人注目的自定义 C++ 分配器示例?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/826569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:28:32  来源:igfitidea点击:

Compelling examples of custom C++ allocators?

c++memory-managementstdmemory-alignmentallocator

提问by Naaff

What are some really good reasons to ditch std::allocatorin favor of a custom solution? Have you run across any situations where it was absolutely necessary for correctness, performance, scalability, etc? Any really clever examples?

放弃std::allocator支持自定义解决方案的真正好理由是什么?您是否遇到过对正确性、性能、可扩展性等绝对必要的情况?有什么非常聪明的例子吗?

Custom allocators have always been a feature of the Standard Library that I haven't had much need for. I was just wondering if anyone here on SO could provide some compelling examples to justify their existence.

自定义分配器一直是我不太需要的标准库的一个特性。我只是想知道 SO 上是否有人可以提供一些令人信服的例子来证明他们的存在是合理的。

采纳答案by timday

As I mention here, I've seen Intel TBB's custom STL allocator significantly improve performance of a multithreaded app simply by changing a single

正如我在这里提到的,我已经看到英特尔 TBB 的自定义 STL 分配器仅通过更改单个

std::vector<T>

to

std::vector<T,tbb::scalable_allocator<T> >

(this is a quick and convenient way of switching the allocator to use TBB's nifty thread-private heaps; see page 7 in this document)

(这是切换分配器以使用 TBB 漂亮的线程私有堆的一种快速方便的方法;请参阅本文档中的第 7 页

回答by Grumbel

One area where custom allocators can be useful is game development, especially on game consoles, as they have only a small amount of memory and no swap. On such systems you want to make sure that you have tight control over each subsystem, so that one uncritical system can't steal the memory from a critical one. Other things like pool allocators can help to reduce memory fragmentation. You can find a long, detailed paper on the topic at:

自定义分配器可以发挥作用的一个领域是游戏开发,尤其是在游戏机上,因为它们只有少量内存且没有交换。在这样的系统上,您希望确保对每个子系统都有严格的控制,这样一个不重要的系统就不能从一个关键的系统中窃取内存。池分配器等其他东西可以帮助减少内存碎片。您可以在以下位置找到有关该主题的长篇详细论文:

EASTL -- Electronic Arts Standard Template Library

EASTL -- 电子艺界标准模板库

回答by Johannes Thoma

I am working on a mmap-allocator that allows vectors to use memory from a memory-mapped file. The goal is to have vectors that use storage that are directly in the virtual memory mapped by mmap. Our problem is to improve reading of really large files (>10GB) into memory with no copy overhead, therefore I need this custom allocator.

我正在研究一个 mmap 分配器,它允许向量使用内存映射文件中的内存。目标是让向量直接使用由 mmap 映射的虚拟内存中的存储。我们的问题是在没有复制开销的情况下改善将真正大文件(> 10GB)读入内存的能力,因此我需要这个自定义分配器。

So far I have the skeleton of a custom allocator (which derives from std::allocator), I think it is a good starting point to write own allocators. Feel free to use this piece of code in whatever way you want:

到目前为止,我已经有了一个自定义分配器的框架(它派生自 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() { }
        };
}

To use this, declare an STL container as follows:

要使用它,请按如下方式声明一个 STL 容器:

using namespace std;
using namespace mmap_allocator_namespace;

vector<int, mmap_allocator<int> > int_vec(1024, 0, mmap_allocator<int>());

It can be used for example to log whenever memory is allocated. What is neccessary is the rebind struct, else the vector container uses the superclasses allocate/deallocate methods.

例如,它可用于在分配内存时记录日志。必要的是重新绑定结构,否则向量容器使用超类分配/解除分配方法。

Update: The memory mapping allocator is now available at https://github.com/johannesthoma/mmap_allocatorand is LGPL. Feel free to use it for your projects.

更新:内存映射分配器现在可在https://github.com/johannesthoma/mmap_allocator获得,并且是 LGPL。随意将它用于您的项目。

回答by Thomas Jones-Low

I'm working with a MySQL storage engine that uses c++ for its code. We're using a custom allocator to use the MySQL memory system rather than competing with MySQL for memory. It allows us to make sure we're using memory as the user configured MySQL to use, and not "extra".

我正在使用一个 MySQL 存储引擎,它的代码使用 C++。我们使用自定义分配器来使用 MySQL 内存系统,而不是与 MySQL 竞争内存。它允许我们确保我们使用的内存是用户配置 MySQL 使用的,而不是“额外”的。

回答by Martin Cote

It can be useful to use custom allocators to use a memory pool instead of the heap. That's one example among many others.

使用自定义分配器来使用内存池而不是堆会很有用。这是众多例子中的一个。

For most cases, this is certainly a premature optimization. But it can be very useful in certain contexts (embedded devices, games, etc).

对于大多数情况,这肯定是过早的优化。但它在某些情况下(嵌入式设备、游戏等)可能非常有用。

回答by pts

I haven't written C++ code with a custom STL allocator, but I can imagine a webserver written in C++, which uses a custom allocator for automatic deletion of temporary data needed for responding to a HTTP request. The custom allocator can free all temporary data at once once the response has been generated.

我没有使用自定义 STL 分配器编写 C++ 代码,但我可以想象一个用 C++ 编写的网络服务器,它使用自定义分配器自动删除响应 HTTP 请求所需的临时数据。自定义分配器可以在生成响应后立即释放所有临时数据。

Another possible use case for a custom allocator (which I have used) is writing a unit test to prove that that a function's behavior doesn't depend on some part of its input. The custom allocator can fill up the memory region with any pattern.

自定义分配器(我使用过)的另一个可能用例是编写单元测试以证明函数的行为不依赖于其输入的某些部分。自定义分配器可以用任何模式填充内存区域。

回答by leander

I'm using custom allocators here; you might even say it was to work aroundother custom dynamic memory management.

我在这里使用自定义分配器;您甚至可能会说这是为了解决其他自定义动态内存管理问题。

Background: we have overloads for malloc, calloc, free, and the various variants of operator new and delete, and the linker happily makes STL use these for us. This lets us do things like automatic small object pooling, leak detection, alloc fill, free fill, padding allocation with sentries, cache-line alignment for certain allocs, and delayed free.

背景:我们有 malloc、calloc、free 以及 operator new 和 delete 的各种变体的重载,链接器很高兴让 STL 为我们使用这些。这让我们可以做一些事情,比如自动小对象池、泄漏检测、分配填充、自由填充、使用哨兵的填充分配、某些分配的缓存行对齐和延迟释放。

The problem is, we're running in an embedded environment -- there isn't enough memory around to actually do leak detection accounting properly over an extended period. At least, not in the standard RAM -- there's another heap of RAM available elsewhere, through custom allocation functions.

问题是,我们在一个嵌入式环境中运行——没有足够的内存来在很长一段时间内正确地进行泄漏检测会计。至少,不是在标准 RAM 中——通过自定义分配函数,在其他地方还有另一个可用的 RAM 堆。

Solution: write a custom allocator that uses the extended heap, and use it onlyin the internals of the memory leak tracking architecture... Everything else defaults to the normal new/delete overloads that do leak tracking. This avoids the tracker tracking itself (and provides a bit of extra packing functionality too, we know the size of tracker nodes).

解决方案:编写一个使用扩展堆的自定义分配器,并在内存泄漏跟踪架构的内部使用它......其他一切都默认为进行泄漏跟踪的正常 new/delete 重载。这避免了跟踪器跟踪自身(并且还提供了一些额外的打包功能,我们知道跟踪器节点的大小)。

We also use this to keep function cost profiling data, for the same reason; writing an entry for each function call and return, as well as thread switches, can get expensive fast. Custom allocator again gives us smaller allocs in a larger debug memory area.

出于同样的原因,我们也使用它来保存功能成本分析数据;为每个函数调用和返回以及线程切换编写一个条目可能会很快变得昂贵。自定义分配器再次在更大的调试内存区域中为我们提供更小的分配。

回答by Sebastian

When working with GPUs or other co-processors it is sometimes beneficial to allocate data structures in main memory in a special way. This special wayof allocating memory can implemented in a custom allocator in a convenient fashion.

在使用 GPU 或其他协处理器时,有时以特殊方式在主内存中分配数据结构是有益的。这种分配内存的特殊方式可以以方便的方式在自定义分配器中实现。

The reason why custom allocation through the accelerator runtime can be beneficial when using accelerators is the following:

使用加速器时,通过加速器运行时进行自定义分配的原因如下:

  1. through custom allocation the accelerator runtime or driver is notified of the memory block
  2. 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
  3. 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
  4. 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)
  1. 通过自定义分配,加速器运行时或驱动程序被通知内存块
  2. 此外,操作系统可以确保分配的内存块是页面锁定的(有些人称之为固定内存),即操作系统的虚拟内存子系统可能不会在内存中或从内存中移动或移除页面
  3. 如果 1. 和 2. 保持并且请求在页面锁定内存块和加速器之间进行数据传输,则运行时可以直接访问主内存中的数据,因为它知道它在哪里并且可以确定操作系统没有移动/移除它
  4. 这节省了在以非页面锁定方式分配的内存时会发生的内存复制:必须将主内存中的数据复制到页面锁定的暂存区,从加速器可以初始化数据传输(通过 DMA )

回答by J?rgen Fogh

I am using a custom allocator for counting the number of allocations/deallocations in one part of my program and measuring how long it takes. There are other ways this could be achieved but this method is very convenient for me. It is especially useful that I can use the custom allocator for only a subset of my containers.

我正在使用自定义分配器来计算我程序的一部分中的分配/解除分配数量并测量需要多长时间。还有其他方法可以实现,但这种方法对我来说非常方便。我可以将自定义分配器仅用于我的容器的一个子集,这一点特别有用。

回答by Stephen

One essential situation: When writing code that must work across module (EXE/DLL) boundaries, it is essential to keep your allocations and deletions happening in only one module.

一种基本情况:在编写必须跨模块 (EXE/DLL) 边界工作的代码时,必须确保分配和删除只发生在一个模块中。

Where I ran into this was a Plugin architecture on Windows. It is essential that, for example, if you pass a std::string across the DLL boundary, that any reallocations of the string occur from the heap where it originated from, NOT the heap in the DLL which may be different*.

我遇到的地方是 Windows 上的插件架构。例如,如果您跨 DLL 边界传递 std::string,则该字符串的任何重新分配都发生在它起源的堆中,而不是 DLL 中可能不同的堆中,这一点很重要*。

*It's more complicated than this actually, as if you are dynamically linking to the CRT this might work anyways. But if each DLL has a static link to the CRT you are heading to a world of pain, where phantom allocation errors continually occur.

*实际上比这更复杂,就好像您正在动态链接到 CRT 一样,这可能无论如何都可以工作。但是,如果每个 DLL 都有一个指向 CRT 的静态链接,那么您将进入一个痛苦的世界,幻象分配错误不断发生。