抛弃std::allocator以支持自定义解决方案的一些真正好的理由是什么?您是否遇到过这样的情况:它对于正确性、性能、可伸缩性等来说是绝对必要的?有什么聪明的例子吗?

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


当前回答

我正在使用一个自定义分配器来计算程序的一部分中的分配/释放的数量,并测量它需要多长时间。还有其他方法可以达到这个目的,但这个方法对我来说非常方便。特别有用的是,我只能对容器的一个子集使用自定义分配器。

其他回答

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

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

对于共享内存来说,不仅容器头存储在共享内存中,而且容器头包含的数据也存储在共享内存中,这一点至关重要。

Boost::Interprocess的分配器就是一个很好的例子。然而,正如你在这里读到的,这个allone是不够的,要使所有STL容器共享内存兼容(由于不同进程中的映射偏移量不同,指针可能会“中断”)。

强制性链接到Andrei Alexandrescu 2015年CppCon关于分配者的演讲:

https://www.youtube.com/watch?v=LIb3L4vKZ7U

好处是,只是设计它们让你想到如何使用它们:-)

自定义分配器是在释放内存之前安全地擦除内存的合理方法。

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)

One example of I time I have used these was working with very resource constrained embedded systems. Lets say you have 2k of ram free and your program has to use some of that memory. You need to store say 4-5 sequences somewhere that's not on the stack and additionally you need to have very precise access over where these things get stored, this is a situation where you might want to write your own allocator. The default implementations can fragment the memory, this might be unacceptable if you don't have enough memory and cannot restart your program.

One project I was working on was using AVR-GCC on some low powered chips. We had to store 8 sequences of variable length but with a known maximum. The standard library implementation of the memory management is a thin wrapper around malloc/free which keeps track of where to place items with by prepending every allocated block of memory with a pointer to just past the end of that allocated piece of memory. When allocating a new piece of memory the standard allocator has to walk over each of the pieces of memory to find the next block that is available where the requested size of memory will fit. On a desktop platform this would be very fast for this few items but you have to keep in mind that some of these microcontrollers are very slow and primitive in comparison. Additionally the memory fragmentation issue was a massive problem that meant we really had no choice but to take a different approach.

So what we did was to implement our own memory pool. Each block of memory was big enough to fit the largest sequence we would need in it. This allocated fixed sized blocks of memory ahead of time and marked which blocks of memory were currently in use. We did this by keeping one 8 bit integer where each bit represented if a certain block was used. We traded off memory usage here for attempting to make the whole process faster, which in our case was justified as we were pushing this microcontroller chip close to it's maximum processing capacity.

在嵌入式系统上下文中,我还可以看到编写自己的自定义分配器的其他情况,例如,如果序列的内存不在主ram中,而在这些平台上可能经常出现这种情况。