自定义C++ STL内存分配器

Posted 天下太平

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义C++ STL内存分配器相关的知识,希望对你有一定的参考价值。

第一种 自定义allocator

C++03时代的STL容器使用的是被称为分配器allocator的内存管理模块。
allocator是无状态(stateless)的,定义里没有成员变量,全是成员函数和一些typedef。
自定义allocator细节很多,尤其是那个rebind。
allocator是个画蛇添足的设计,提供了不必要的灵活性,增加了复杂度,增加了心智负担。

stl_mem.h
#pragma once

#include <iostream>
#include <list>
#include <map>
#include <unordered_map>
#include <vector>
#include <set>
#include <algorithm>
#include <mutex>
using namespace std;
namespace MEM_STL 
    struct memNode
    
        memNode *nextnode;
    ;

    class mempool
    
    public:
        static void *alloc(int size)
        
            printf("alloc:%d\\n", size);

            lock_guard<mutex> guard(m_mutex);

            int index = getindex(size);
            int realsize = 1 << (index + 1);
            if (m_free_head[index] == NULL)
            
                return malloc(realsize);
            
            else
            
                void *p = m_free_head[index];
                m_free_head[index] = m_free_head[index]->nextnode;
                return p;
            

            return NULL;
        
        static void delloc(void *ptr, int size)
        
            printf("delloc:%d\\n", size);

            lock_guard<mutex> guard(m_mutex);

            int index = getindex(size);
            memNode *pNew = (memNode *)ptr;
            pNew->nextnode = m_free_head[index];
            m_free_head[index] = pNew;
        
        static void report()
        
            lock_guard<mutex> guard(m_mutex);
            printf("mempool report\\n");
            for (int i = 0; i < 32; ++i)
            
                int n = 0;
                auto p = m_free_head[i];
                while (p)
                
                    n++;
                    p = p->nextnode;
                
                printf("index|%02d|len|%d\\n", i, n);
            
        
    private:
        static int getindex(int size)
        
            static const unsigned int sizetable[32] =
            
                1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, 1 << 8, 1 << 9, 1 << 10,
                1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20,
                1 << 21, 1 << 22, 1 << 23, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29, 1 << 30,
                (unsigned int)1 << 31, 0xFFFFFFFF
            ;

            auto p = lower_bound(sizetable, sizetable + 32, (unsigned int)size);
            return  distance(sizetable, p);
        

    protected:
        static memNode *m_free_head[32];   //32个指针
        static mutex m_mutex;
    ;

    template<class T>
    class my_allocator : public std::allocator<T>
    
    public:
        typedef std::allocator<T> base_type;
        typedef size_t     size_type;
        typedef T *pointer;

        // 必须要重新定义
        template<class Other>
        struct rebind
        
            typedef my_allocator<Other> other;
        ;

        // 构造函数必须实现
        my_allocator()
        
        

        my_allocator(my_allocator<T> const &)
        
        

        my_allocator<T> &operator=(my_allocator<T> const &)
        
            return (*this);
        

        // 模板
        template<class Other>
        my_allocator(my_allocator<Other> const &)
        
        

        // 模板
        template<class Other>
        my_allocator<T> &operator=(my_allocator<Other> const &)
        
            return (*this);
        

        // 内存的分配与释放可以实现为自定义的算法,替换函数体即可
        pointer allocate(size_type count)
        
            return (pointer)mempool::alloc(count * sizeof(T));
        

        void deallocate(pointer ptr, size_type count)
        
            mempool::delloc(ptr, count * sizeof(T));
        
    ;

    template<class _Ty, class _Alloc = my_allocator<_Ty> >
    class m_list : public list<_Ty, _Alloc>
    
    ;

    template<class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = my_allocator<pair<const _Kty, _Ty> > >
    class m_map : public map<_Kty, _Ty, _Pr, _Alloc>
    
    ;

    template<class _Ty, class _Alloc = my_allocator<_Ty>>
    class m_vector : public vector<_Ty, _Alloc>
    
    ;

    template<class _Kty, class _Pr = less<_Kty>, class _Alloc = my_allocator<_Kty> >
    class m_set : public set<_Kty, _Pr, _Alloc>
    
    ;

    template<class _Kty, class _Ty, class _HASH = hash<_Kty>, class _KeyEqual = equal_to<_Kty>, class _Alloc = my_allocator<_Kty>>
    class m_unordered_map : public unordered_map<_Kty, _Ty, _HASH, _KeyEqual, _Alloc>
    
    ;
;

stl_mem.cpp
#include "stl_mem.h"
using namespace MEM_STL;

memNode *mempool::m_free_head[32] =  0 ;
mutex mempool::m_mutex =  ;

于是C++17引入了pmr(polymorphic memory resource 多态的内存资源)。

第二种 使用C++17提供的pmr

先看一种非常简单的内存分配实现:

char *buf = 0;
const int max_size = 1024000;
int len = 0;

void *new_(size_t t)
    void *p = 0;
    if (t + len < max_size)
        p = &buf[len];
        len += (t + 4);
    
    return p;

void free_(void *p)

class buf_helper
public:
    buf_helper()
        buf = new char[max_size];
    
    ~buf_helper()
        delete buf;
    
;
buf_helper g_buf_helper;

new一块内存,中途只使用不回收,最后一次性delete掉。
pmr就是这种思路。C++17 pmr

点击查看代码
#include <iostream>
#include <cstdlib>
#include <iostream>
#include <memory_resource>
#include <vector>

char *buf = 0;
const int max_size = 1024000;
int len = 0;

void *new_(size_t t)

    void *p = 0;
    if (t + len < max_size)
    
        p = &buf[len];
        len += (t + 4);
    
    return p;

void free_(void *p)



class buf_helper

public:
    buf_helper()
    
        buf = new char[max_size];
    
    ~buf_helper()
    
        delete buf;
    
;
buf_helper g_buf_helper;


class debug_resource : public std::pmr::memory_resource

public:
    explicit debug_resource(std::pmr::memory_resource *up = std::pmr::get_default_resource())
        : _upstream up 
    
    

    void *do_allocate(size_t bytes, size_t alignment) override
    
        std::cout << " do_allocate(): " << bytes << \'\\n\';
        return _upstream->allocate(bytes, alignment);
    
    void do_deallocate(void *ptr, size_t bytes, size_t alignment) override
    
        std::cout << " do_deallocate(): " << bytes << \'\\n\';
        _upstream->deallocate(ptr, bytes, alignment);
    
    bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override
    
        std::cout << " do_is_equal(): " << \'\\n\';
        return this == &other;
    

private:
    std::pmr::memory_resource *_upstream;
;

int main()

    char buffer[32] = ;
    std::fill_n(std::begin(buffer), std::size(buffer) - 1, \'_\');
    std::cout << buffer << \'\\n\';
    debug_resource dr;

    std::pmr::monotonic_buffer_resource pool std::data(buffer), std::size(buffer) ,&dr ;

    std::pmr::vector<char> vec &pool ;
    //vec.reserve(26);

    for (char ch = \'a\'; ch <= \'z\'; ++ch)
    
        vec.push_back(ch);
        std::cout << buffer << \'\\n\';

    

    std::cout << buffer << \'\\n\';



    

核心代码

char buffer[32] = ;
memory_resource dr;
std::pmr::monotonic_buffer_resource pool std::data(buffer), std::size(buffer) ,&dr ;
std::pmr::vector<char> vec &pool ;

pmr::memory_resource 决定内存来源

monotonic buffer resource
unsynchronized_pool_resource
synchronized_pool_resource

pmr::polymorphic_allocator 负责内存分配

名字中的polymorphic是说:这个新的分配器的行为会表现出多态,与以前的分配器不同。
新分配器和stl分配器兼容。保存了一个memory_resource的指针,所以是有状态的(statefull)。
外界需要保证memory_resource指针的生命周期。
polymorphic_allocator 就是把Memory_resource适配成Alloctor。

pmr是专门用来提高c++内存性能的!一般应用,对pmr的需求没有那么强烈。

一句话:内存局部性原理

局部性原理--各类优化的基石

可以细读这篇文章:游戏引擎开发新感觉!(6) c++17内存管理

以上是关于自定义C++ STL内存分配器的主要内容,如果未能解决你的问题,请参考以下文章

STL容器自定义内存分配器

STL容器自定义内存分配器

STL容器自定义内存分配器

STL 容器的自定义分配器错误

自定义堆栈分配器中的 C++ 内存对齐

C++学习之旅第六站:让我们一起走进 STL库