【C++】高并发内存池(项目)

此项目原型是Google的一个开源项目tcmalloc(Thread-Caching Malloc)即线程缓存的malloc,实现了高效的多线程内存管理,用于替代系统的内存分配相关的函数;

一、内存池

1.池化技术
程序现象系统申请过量的资源,然后自己管理。之所以要申请过量的资源,是因为每次申请该资源都会有较大的开销,提前申请好后,会大大提高程序运行效率。
除了有内存池,还有连接池、线程池、对象池等。想城池主要思想是:县启动若干数量的线程,让他们处于睡眠状态,当接收到客户端的请求时,唤醒池中某个睡眠的线程,让他来处理客户端的请求,档处理完成后,线程再次进入睡眠状态
2.内存池
内存池是指程序预先从操作系统中申请一块足够大的内存,当程序需要申请内存时,不直接向操作系统申请,而是直接从内存池中获取;当程序是方式,并不将内存返回给操作系统,而是返回内存池。当程序退出(或特定时间)时,内存池才将之前申请的内存真正释放。
3.内存池主要解决的问题
内存池主要解决的是现率问题,还需要解决内存碎片问题;
【C++】高并发内存池(项目)_第1张图片
内存碎片分为内碎片和外碎片,外碎片是一些空闲的连续内存区域太小,这些内存空间不连续,以至于合集的内存足够,但是不能满足一些的内存分配申请需求。内部碎片式由于一些对齐的需求,导致分配出去的空间中一些内存无法被利用。
4.malloc
C/C++中动态申请内存都是使用malloc,而malloc就是一个内存池,malloc() 相当于向操作系统“批发”了一块较大的内存空间,然后“零售”给应用程序。当全部用完或程序有大量的内存需求时,再根据实际情况向操作系统申请内存,linux中的gcc用的是glibc中的ptmalloc。
【C++】高并发内存池(项目)_第2张图片

二、设计一个定长内存池

#include 
using std::cout;
using std::endl;

//定长内存池控制大小
//template
//class ObjectPool
//{
//
//};

template
class ObjectPool
{
public:
    T* New()
    {
        T* obj = nullptr;

        if (_freeList)
        {
            void* next = *((void**)_freeList);
            obj = (T*)_freeList;
            _freeLost = next;
        }
        else
        {
            if (_remainBytes < sizeof(T))
            {
                _remainBytes= 128 * 1024
                _memory = (char*)malloc(_remainBytes);
                if (_memory == nullptr)
                {
                    throw std::bad_alloc();
                }
            }
            obj = (T*)_memory;
            size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
            _memory += objSize;
            _remainBytes -= sizeof(T);
        }
        //定位new,显示调用T的构造函数初始化;
        new(obj)T;

        return obj;
    }

    void Delete(T* obj)
    {
        //显示调用析构函数,清理对象
        obj->~T();

        if (_freeList == nullptr)
        {
            _freeList = obj;
            *(void**)obj = nullptr;
        }
    }

private: 
    char* _memory = nullptr;//使用char类型比较方便
    size_T _remainBytes = 0;//剩余字节数;
    void* _freeList = nullptr;//需要构建自由链表,帮助存储使用后返回的内存块,一般在内存块的头几个字节中存储返回字节的地址。

};

三、高并发内存池整体框架设计

目前许多开发场景都是多核多线程,在申请内存的场景下,存在激烈的所竞争问题,tcmalloc在多线程高并发场景下更好用,所以实现的内存池需要考虑以下方面:
1.性能问题
2.多线程环境下,锁的竞争问题
3.内存碎片问题;

高并发内存池主要由3各部分组成:
1.thread cache:线程缓存是每个线程独有的,用于小于256kb的内存分配,线程从这里申请内存不需要加锁,每个线程独享一个cache,这就是并发线程池高效的地方;
2.central cache:中心缓存是所有线程锁共享,Thread cache是按需从central cache中获取的对象。central合适的试剂回收thread cache中的对象,避免一个线程占用了太多的内存,而其他线程的内存不够用,达到内存分配在多线程中更均衡的按需调度的目的。central cache 是存在竞争的所以从这里取内存对象是需要加锁的。首先者利用的是桶锁,其次只有thread cache的没有内存对象时才会找central cache,所以这里竞争不会很激烈。
3.page cache:页缓存是在central cache缓存上面的一层缓存,存储的内存是以页单位存储及分配的,central cache没有内存对象时,从page cache分配处一定数量的page,并切割成定长大小的小块内存,分配给central cache。当一个span的几个跨度页的对象都回收以后,page cache会回收central cache满足条件的span对象,并且合并相邻的页,组成更大的页,缓解内存碎片的问题。

四、thread cache 设计

Common.h

#pragma once

#include 
#include 
#include 
#include 
using std::cout;
using std::endl;

static const size_t MAX_BYTES = 256 * 1024;
static const size_t NFREELIST = 208;

void*& NextObj(void* obj)
{
    return *(void**)obj;
}

//管理切分好的小对象的自由链表
class FreeList
{
public:
    void Push(void* obj)
    {
        assert(obj);
        //头插
        //*(void**)obj = _freeList;
        NextObj(obj) = _freeList;
        _freeList = obj;
    }

    void* Pop()
    {
        assert(_freeList);
        //头删
        void* obj = _freeList;
        _freeList = NextObj(obj);
    }

    bool Empty()
    {
        return _freeList == nullptr;
    }
private:
    void* _freeList;
};

//计算对象大小的对齐映射规则
//[1,128]                         8byte对齐               freelist[0,16)
//[128+1,1024]                    16byte对齐              freelist[16,72)
//[1024+1,8*1024]                 128byte对齐             freelist[72,128)
//[8*1024+1,64*1024]              1024byte对齐            freelist[128,184)
//[64*1024+1,256*1024]            8*1024byte对齐          freelist[184,208)
//
class SizeClass
{
public:
    /*size_t _RoundUp(size_t size; size_t alignNum)
    {
        size_t alignSize;
        if (size % alignNum != 0)
        {
            alignSize = size / (alignNum + 1)*alignNum;
        }
        else
        {
            alignSize = size;
        }

        return alignSize;
    }*/

    static inline size_t _RoundUp(size_t bytes, size_t alignNum)
    {
        return ((bytes + alignNum - 1) & ~(alignNum - 1));
    }

    static inline size_t RoundUp(size_t size)
    {
        if (size <= 128)
        {
            return _RoundUp(size, 8);
        }
        else if (size <= 1024)
        {
            return _RoundUp(size, 16);
        }
        else if (size <= 8 * 1024)
        {
            return _RoundUp(size, 128);
        }
        else if (size <= 64 * 1024)
        {
            return _RoundUp(size, 1024);
        }
        else if (size <= 256 * 1024)
        {
            return _RoundUp(size, 8*1024);
        }
        else
        {
            assert(false);
            return -1;
        }
    }

    /*size_t _Index(size_t bytes, size_t alignNum)
    {
        if (bytes%alignNum==0)
        {
            return bytes / alignNum - 1;
        }
        else
        {
            return bytes / alignNum;
        }
    }*/

    static inline size_t _Index(size_t bytes, size_t align_shift)
    {
        return ((bytes + (1 << align_shift) - 1) >> align_shift) - 1;
    }

    static inline size_t Index(size_t bytes)
    {
        assert(bytes <= MAX_BYTES);
        static int group_array[4] = { 16,56,56,56 };
        if (bytes <= 128)
        {
            return _Index(bytes, 3);
        }
        else if (bytes <= 1024)
        {
            return _Index(bytes - 128, 4) + group_array[0];
        }
        else if (bytes <= 8 * 1024)
        {
            return _Index(bytes - 1024, 7) + group_array[1] + group_array[0];
        }
        else if (bytes <= 64 * 1024)
        {
            return _Index(bytes - 8 * 1024, 10) + group_array[2] + group_array[1] + group_array[0];
        }
        else if (bytes <= 256 * 1024)
        {
            return _Index(bytes - 64 * 1024, 10) + group_array[3] + group_array[2] + group_array[1] + group_array[0];
        }
        else
        {
            assert(false);
        }
        return -1;
    }

};

ThreadCache.h

#pragma once

#include "Common.h"

class ThreadCache
{
public:
    //申请和释放空间
    void* Allocate(size_t size);

    void Deallocate(void* ptr, size_t size);

private:
    FreeList _freeLists[NFREELIST];
};

ThreadCache.cpp

#include "ThreadCache.h"

void* ThreadCache::Allocate(size_t size)
{
    assert(size <= MAX_BYTES);
    size_t alignSize = SizeClass::RoundUp(size);
    size_t index = SizeClass::Index(size);

    if (!_freeLists[index].Empty())
    {
        return _freeLists[index].Pop();
    }
    else
    {
        return FetchFromCentralCache(index,alignSize);
    }

}

void ThreadCache::Deallocate(void* ptr, size_t size)
{

}

你可能感兴趣的:(C++,c++)