高并发内存池(centralcache)[2]

Central cache

threadcache是每个线程独享,而centralcache是多线程共享,需要加锁(桶锁)一个桶一个锁

解决外碎片问题:内碎片:申请大小超过实际大小;外碎片:空间碎片不连续,导致无法申请大块空间

span

在存储管理中,"span"是一个术语,用于描述连续的内存块或磁盘块的范围。

在内存管理中,一个span通常是一系列连续的内存页或内存块,它们被分配给一个进程或数据结构。一个span的大小可以根据需求而变化,通常是以页的大小为单位进行分配。

在磁盘管理中,一个span通常是一系列连续的磁盘块,它们被分配给一个文件或数据结构。一个span的大小可以根据需求而变化,通常是以磁盘块的大小为单位进行分配。

使用span的好处是可以提高内存或磁盘的利用率,避免了碎片化的问题。通过分配连续的span,可以更有效地利用存储资源,并提高数据的读写性能。

span设计成双向链表 带头双向循环,插入删除更加高效

32位机器和64位机器的页数不同!!!!!!!!!

使用条件编译在预处理阶段解决

cpp 复制代码
#ifdef _WIN32
	typedef size_t PAGE_ID;
#elif  _WIN64
	typedef unsigned long long PAGE_ID;
#endif

但是在64位下还是会有问题

所有要调换顺序

cpp 复制代码
#ifdef _WIN64
	typedef unsigned long long PAGE_ID;
#elif  _WIN32
	typedef size_t PAGE_ID;
#endif
cpp 复制代码
// 管理多个连续页大块内存跨度结构
struct Span
{
	PAGE_ID _pageId = 0; // 大块内存起始页的页号
	size_t  _n = 0;      // 页的数量

	Span* _next = nullptr;	// 双向链表结构
	Span* _prev = nullptr;

	size_t _useCount = 0; // 切好小块内存,被分配给thread cache的计数
	void* _freeList = nullptr;  // 切好的小块内存的自由链表
};

// 带头双向循环链表 
class SpanList
{
public:
	SpanList()
	{
		_head = new Span;
		_head->_next = _head;
		_head->_prev = _head;
	}

	Span* Begin()
	{
		return _head->_next;
	}

	Span* End()
	{
		return _head;
	}

	bool Empty()
	{
		return _head->_next == _head;
	}

	void PushFront(Span* span)
	{
		Insert(Begin(), span);
	}

	Span* PopFront()
	{
		Span* front = _head->_next;
		Erase(front);
		return front;
	}

	void Insert(Span* pos, Span* newSpan)
	{
		assert(pos);
		assert(newSpan);

		Span* prev = pos->_prev;
		// prev newspan pos
		prev->_next = newSpan;
		newSpan->_prev = prev;
		newSpan->_next = pos;
		pos->_prev = newSpan;
	}

	void Erase(Span* pos)
	{
		assert(pos);
		assert(pos != _head); //不能删除哨兵位

		Span* prev = pos->_prev;
		Span* next = pos->_next;

		prev->_next = next;
		next->_prev = prev;
		//不去删除,因为空间是要还给下一层
	}

private:
	Span* _head;
public:
	std::mutex _mtx; // 桶锁
};

CentralCache类

只能有一个,所以采用单例模式

cpp 复制代码
// 单例模式
class CentralCache
{
public:
	static CentralCache* GetInstance()
	{
		return &_sInst;
	}

	// 获取一个非空的span
	Span* GetOneSpan(SpanList& list, size_t byte_size);

	// 从中心缓存获取一定数量的对象给thread cache
	size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size);

private:
	SpanList _spanLists[NFREELIST];

private:
	CentralCache()
	{}

	CentralCache(const CentralCache&) = delete;

	static CentralCache _sInst;
};

threadcache从centralcache中获取span

首先明确一次获取的数量

cpp 复制代码
// 一次thread cache从中心缓存获取多少个
	static size_t NumMoveSize(size_t size)
	{
		assert(size > 0);

		// [2, 512],一次批量移动多少个对象的(慢启动)上限值
		// 小对象一次批量上限高
		// 小对象一次批量上限低
		int num = MAX_BYTES / size;
		if (num < 2)
			num = 2;

		if (num > 512)
			num = 512;

		return num;
	}

实现获取,逐步递增。获取一个时直接返回就行,不是一个时先将其串联起来再返回头

cpp 复制代码
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	// 慢开始反馈调节算法
	// 1、最开始不会一次向central cache一次批量要太多,因为要太多了可能用不完
	// 2、如果你不要这个size大小内存需求,那么batchNum就会不断增长,直到上限
	// 3、size越大,一次向central cache要的batchNum就越小
	// 4、size越小,一次向central cache要的batchNum就越大
	size_t batchNum = min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));
	if (_freeLists[index].MaxSize() == batchNum)
	{
		_freeLists[index].MaxSize() += 1;
	}

	void* start = nullptr;
	void* end = nullptr;
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(actualNum > 0);

	if (actualNum == 1)
	{
		assert(start == end);
		return start;
	}
	else
	{
		_freeLists[index].PushRange(NextObj(start), end);
		return start;
	}
}

完善自由链表

cpp 复制代码
//管理切分好的小对象的自由链表
class FreeList
{
public:
	void Push(void* obj)
	{
		assert(obj);

		// 头插
		//*(void**)obj = _freeList;
		NextObj(obj) = _freeList;
		_freeList = obj;
	}

	void PushRange(void* start, void* end)
	{
		NextObj(end) = _freeList;
		_freeList = start;
	}

	void* Pop()
	{
		assert(_freeList);

		// 头删
		void* obj = _freeList;
		_freeList = NextObj(obj);

		return obj;
	}

	bool Empty()
	{
		return _freeList == nullptr;
	}

	size_t& MaxSize()
	{
		return _maxSize;
	}
private:
	void* _freeList;
	size_t _maxSize = 1;
};

实现FetchRangobj

cpp 复制代码
// 从中心缓存获取一定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();

	Span* span = GetOneSpan(_spanLists[index], size);
	assert(span);
	assert(span->_freeList);

	// 从span中获取batchNum个对象
	// 如果不够batchNum个,有多少拿多少
	start = span->_freeList;
	end = start;
	size_t i = 0;
	size_t actualNum = 1;
	while (i < batchNum - 1 && NextObj(end) != nullptr)
	{
		end = NextObj(end);
		++i;
		++actualNum;
	}
	span->_freeList = NextObj(end);
	NextObj(end) = nullptr;
	span->_useCount += actualNum;

	_spanLists[index]._mtx.unlock();

	return actualNum;
}
相关推荐
xiaolang_8616_wjl2 小时前
c++文字游戏_闯关打怪2.0(开源)
开发语言·c++·开源
夜月yeyue2 小时前
设计模式分析
linux·c++·stm32·单片机·嵌入式硬件
无小道3 小时前
c++-引用(包括完美转发,移动构造,万能引用)
c语言·开发语言·汇编·c++
FirstFrost --sy5 小时前
数据结构之二叉树
c语言·数据结构·c++·算法·链表·深度优先·广度优先
Tanecious.5 小时前
C++--map和set的使用
开发语言·c++
Yingye Zhu(HPXXZYY)5 小时前
Codeforces 2021 C Those Who Are With Us
数据结构·c++·算法
liulilittle6 小时前
LinkedList 链表数据结构实现 (OPENPPP2)
开发语言·数据结构·c++·链表
无聊的小坏坏6 小时前
三种方法详解最长回文子串问题
c++·算法·回文串
山河木马7 小时前
前端学习C++之:.h(.hpp)与.cpp文件
前端·javascript·c++
2401_891957317 小时前
list的一些特性(C++)
开发语言·c++