紧跟C++项目高并发内存池_三层缓存
当申请内存小于256KB时,走前三层缓存获得内存。
当申请内存大小大于256KB时就需要新的方法进行申请。
256KB=32页(一页定义为8KB),直接向PageCache申请,并且记录申请的内存页号与span的对于关系。
如果大于PageCache中哈希桶的大小128页时,直接找系统堆申请内存
ThreadCache中哈希桶的个数为208个,最大可以申请到256KB
static const size_t MAX_BYTE = 256 * 1024;
static const size_t NUMLIST = 208;
static const size_t NPAGE = 129;
static const size_t PAGESIZE = 13;
注意:直接向PageCache或堆申请内存时需要加锁。
对于释放内存,如果大于128页,说明直接向系统堆上开辟的空间,先找到这个地址对应的span,再复用之前的PageCache释放空间的函数释放内存。
释放直接向堆申请的空间为:
inline static void SystemFree(void* ptr) {
#ifdef _WIN32
VirtualFree(ptr, 0, MEM_RELEASE);
#else
#endif
}
所以直接涉及到线程调用申请函数方面以及PageCache方面,其余层不需要改变代码。
C++代码
Commen.h:添加了向直接向堆中释放空间SystemFree函数
#pragma once
#include<iostream>
#include<vector>
#include<time.h>
#include<assert.h>
#include<thread>
#include<mutex>
#include<algorithm>
#include<unordered_map>
#include<windows.h>
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
#endif
if (ptr == nullptr)
throw std::bad_alloc();
return ptr;
}
inline static void SystemFree(void* ptr) {
#ifdef _WIN32
VirtualFree(ptr, 0, MEM_RELEASE);
#else
#endif
}
using std::cout; using std::endl;
static const size_t MAX_BYTE = 256 * 1024;
static const size_t NUMLIST = 208;
static const size_t NPAGE = 129;
static const size_t PAGESIZE = 13;
#ifdef _WIN64
typedef unsigned long long PAGE_ID;
#elif _WIN32
typedef size_t PAGE_ID;
#else
#endif
static void*& NextObj(void* obj) {
return *(void**)obj;
}
class FreeList {
public:
FreeList() :_freeList(nullptr), MaxSize(1), size(0) {}
void Push(void* obj) {
assert(obj);
NextObj(obj) = _freeList;
_freeList = obj;
++size;
}
void* Pop() {
assert(_freeList);
void* obj = _freeList;
_freeList = NextObj(obj);
--size;
return obj;
}
void PushList(void* begin, void* end, size_t len) {
NextObj(end) = _freeList;
_freeList = begin;
size += len;
}
void PopList(void*& begin, void*& end, size_t len) {
assert(len <= size);
begin = _freeList; end = begin;
for (size_t i = 0; i < len - 1; i++) {
end = NextObj(end);
}
_freeList = NextObj(end);
NextObj(end) = nullptr;
size -= len;
}
bool Empty() { return _freeList == nullptr; }
size_t GetSize() { return size; }
size_t& GetMaxSize() { return MaxSize; }
private:
void* _freeList;
size_t MaxSize;
size_t size;
};
class SizeClass {
public:
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{
return _RoundUp(size, 1 << PAGESIZE);
}
}
static inline size_t Index(size_t size) {
assert(size <= MAX_BYTE);
static int Group[4] = { 16,56,56,56 };
if (size <= 128) return _Index(size, 8);
else if (size <= 1024) return _Index(size - 128, 16) + Group[0];
else if (size <= 8 * 1024) return _Index(size - 1024, 128) + Group[1] + Group[0];
else if (size < 64 * 1024) return _Index(size - 8 * 1024, 1024) + Group[2] + Group[1] + Group[0];
else if (size < 256 * 1024) return _Index(size - 64 * 1024, 8 * 1024) + Group[3] + Group[2] + Group[1] + Group[0];
else {
assert(false);
return -1;
}
}
static size_t ForMemory(size_t size) {
assert(size > 0);
size_t num = MAX_BYTE / size;
if (num < 2) {
num = 2;
}
else if (num > 512) {
num = 512;
}
return num;
}
static size_t NumForPage(size_t size) {
size_t NumForMemory = ForMemory(size);
size_t Byte = NumForMemory * size;
size_t NumPage = (Byte >> PAGESIZE);
if (NumPage == 0) {
NumPage = 1;
}
return NumPage;
}
private:
static inline size_t _RoundUp(size_t size, size_t AlignNum) {
size_t AligSize = size;
if (size % AlignNum != 0) {
AligSize = (size / AlignNum + 1) * AlignNum;
}
return AligSize;
}
static inline size_t _Index(size_t size, size_t AlignNum) {
size_t Pos = 0;
if (size % AlignNum == 0) {
Pos = size / AlignNum - 1;
}
else {
Pos = size / AlignNum;
}
return Pos;
}
};
struct Span {
PAGE_ID _PageID;
size_t _Num;
Span* _next;
Span* _prev;
size_t use_count;
void* FreeList;
bool IsUse;
Span() :_PageID(0), _Num(0), _next(nullptr), _prev(nullptr), use_count(0), FreeList(nullptr), IsUse(false) {}
};
class SpanList {
private:
Span* _head;
public:
std::mutex _mtx;
SpanList() {
_head = new Span;
_head->_next = _head;
_head->_prev = _head;
}
void Insert(Span* pos, Span* NewSpan) {
assert(pos != nullptr && NewSpan != nullptr);
Span* prev = pos->_prev;
prev->_next = NewSpan;
NewSpan->_prev = prev;
NewSpan->_next = pos;
pos->_prev = NewSpan;
}
Span* Pop() {
Span* front = _head->_next;
Erase(_head->_next);
return front;
}
void Erase(Span* pos) {
assert(pos != _head);
Span* prev = pos->_prev;
Span* next = pos->_next;
prev->_next = next;
next->_prev = prev;
}
Span* begin() { return _head->_next; }
Span* end() { return _head; }
bool Empty() { return _head->_next == _head; }
};
ConcurrentAlloc.h:添加了线程申请超过256KB空间情况
#pragma once
#include"Common.h"
#include"ThreadCache.h"
#include"PageCache.h"
static void* ConcurrentAlloc(size_t size) {
if (size > MAX_BYTE) {
size_t AlignSize = SizeClass::RoundUp(size);
size_t K = AlignSize >> PAGESIZE;
PageCache::GetInst()->_PageMtx.lock();
Span*span=PageCache::GetInst()->NewSpan(K);
PageCache::GetInst()->_PageMtx.unlock();
void* ptr = (void*)(span->_PageID << PAGESIZE);
return ptr;
}
else {
if (tls_threadcache == nullptr) {
tls_threadcache = new ThreadCache;
}
cout << std::this_thread::get_id() << " " << tls_threadcache << endl;
return tls_threadcache->ApplySpace(size);
}
}
static void ConcurrentFree(void* ptr, size_t size) {
if(size>MAX_BYTE){
Span* span = PageCache::GetInst()->MapObjToSpan(ptr);
PageCache::GetInst()->_PageMtx.lock();
PageCache::GetInst()->RetrunPageCache(span);
PageCache::GetInst()->_PageMtx.unlock();
}
else {
assert(tls_threadcache != nullptr);
tls_threadcache->ReleaseSpace(ptr, size);
}
}
PageCache:申请Span和释放内存过程添加了大于32页的情况
#pragma once
#include"Common.h"
class PageCache {
private:
SpanList _SpanList[NPAGE];
static PageCache _sInst;
std::unordered_map<PAGE_ID, Span*>IdSpanMap;
PageCache() {}
public:
std::mutex _PageMtx;
PageCache(const PageCache&) = delete;
Span* MapObjToSpan(void* obj);
static PageCache* GetInst() {
return &_sInst;
}
void RetrunPageCache(Span* span);
Span* NewSpan(size_t NumPage);
};
#include"PageCache.h"
PageCache PageCache::_sInst;
Span* PageCache::NewSpan(size_t NumPage) {
assert(NumPage > 0);
if (NumPage >= NPAGE) {
void* ptr = SystemAlloc(NumPage);
Span* span = new Span;
span->_PageID = (PAGE_ID)ptr >> PAGESIZE;
span->_Num = NumPage;
IdSpanMap[span->_PageID] = span;
return span;
}
else {
if (!_SpanList[NumPage].Empty()) {
return _SpanList[NumPage].Pop();
}
for (size_t i = NumPage + 1; i < NPAGE; i++) {
if (!_SpanList[i].Empty()) {
Span* NumPageSpan = new Span;
Span* NSpan = _SpanList[i].Pop();
NumPageSpan->_PageID = NSpan->_PageID;
NumPageSpan->_Num = NumPage;
NSpan->_PageID += NumPage;
NSpan->_Num -= NumPage;
_SpanList[NSpan->_Num].Insert(_SpanList[NSpan->_Num].begin(), NSpan);
IdSpanMap[NSpan->_PageID] = NSpan;
IdSpanMap[NSpan->_PageID + NSpan->_Num - 1] = NSpan;
for (PAGE_ID i = 0; i < NumPageSpan->_Num; i++) {
IdSpanMap[NumPageSpan->_PageID + i] = NumPageSpan;
}
return NumPageSpan;
}
}
Span* BigSpan = new Span;
void* ptr = SystemAlloc(NPAGE - 1);
BigSpan->_PageID = (PAGE_ID)ptr >> PAGESIZE;
BigSpan->_Num = NPAGE - 1;
_SpanList[BigSpan->_Num].Insert(_SpanList[BigSpan->_Num].begin(), BigSpan);
return NewSpan(NumPage);
}
}
Span* PageCache::MapObjToSpan(void* obj) {
PAGE_ID pageId = (PAGE_ID)obj >> PAGESIZE;
std::unordered_map<PAGE_ID,Span*>::iterator ret = IdSpanMap.find(pageId);
if (ret != IdSpanMap.end()) {
return ret->second;
}
else {
assert(false);
return nullptr;
}
}
void PageCache::RetrunPageCache(Span* span) {
if (span->_Num >= NPAGE) {
void* ptr = (void*)(span->_PageID << PAGESIZE);
SystemFree(ptr);
delete span;
}
else {
while (true) {
PAGE_ID prevId = span->_PageID - 1;
std::unordered_map<PAGE_ID, Span*>::iterator ret = IdSpanMap.find(prevId);
if (ret == IdSpanMap.end()) {
break;
}
else {
if (ret->second->IsUse == true) {
break;
}
else if (ret->second->_Num + span->_Num >= NPAGE) {
break;
}
else {
span->_PageID = ret->second->_PageID;
span->_Num += ret->second->_Num;
_SpanList[ret->second->_Num].Erase(ret->second);
delete ret->second;
}
}
}
while (true) {
PAGE_ID nextId = span->_PageID + span->_Num;
std::unordered_map<PAGE_ID, Span*>::iterator ret = IdSpanMap.find(nextId);
if (ret == IdSpanMap.end()) {
break;
}
else {
Span* nextSpan = ret->second;
if (nextSpan->IsUse == true) {
break;
}
else if (nextSpan->_Num + span->_Num >= NPAGE) {
break;
}
else {
span->_Num += nextSpan->_Num;
_SpanList[nextSpan->_Num].Erase(nextSpan);
IdSpanMap.erase(nextSpan->_Num);
delete nextSpan;
}
}
}
_SpanList[span->_Num].Insert(_SpanList[span->_Num].begin(), span);
span->IsUse = false;
IdSpanMap[span->_PageID] = span;
IdSpanMap[span->_PageID + span->_Num - 1] = span;
}
}
单线程测试
#include"ConcurrentAlloc.h"
void TestBigAlloc() {
void* ptr = ConcurrentAlloc(32 * 1024 * 8 + 1);
void* ptr1 = ConcurrentAlloc(128 * 1024 * 8 + 1);
ConcurrentFree(ptr, 32 * 1024 * 8 + 1);
ConcurrentFree(ptr1, 128 * 1024 * 8 + 1);
}
int main()
{
TestBigAlloc();
return 0;
}
至此,项目全部写完,后序过程就是对项目进行性能测试及其优化。
代码位置
Github Gitee
|