【lesson12】高并发内存池项目最终完整版代码

文章目录

  • Common.h
  • ObjectPool.h
  • ThreadCache.h
  • ThreadCache.cpp
  • ConcurrentAlloc.h
  • CentralCache.h
  • CentralCache.cpp
  • PageMap.h
  • PageCache.h
  • PageCache.cpp
  • 测试代码BenchMark.cpp

Common.h

#pragma once
#include 
#include 
#include 
#include 

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

#ifdef _WIN64
typedef unsigned long long PAGE_ID;
#elif _WIN32
typedef size_t PAGE_ID;
#else
// linux
#endif

#ifdef _WIN32
#include
#else
// 
#endif

static const size_t MAX_BYTES = 256 * 1024;
static const size_t TCFREELIST = 208;
static const size_t CCFREELIST = 208;
static const size_t PCSPANLIST = 129;
static const size_t PAGE_SHIFT = 13;

// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
	void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
	// linux下brk mmap等
#endif

	if (ptr == nullptr)
		throw std::bad_alloc();

	return ptr;
}

inline static void SystemFree(void* ptr)
{
#ifdef _WIN32
	VirtualFree(ptr, 0, MEM_RELEASE);
#else
	// sbrk unmmap等
#endif
}

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

class FreeList
{
public:
	void Push(void* obj)
	{
		assert(obj);
		//头插
		NextObj(obj) = _freelist;
		_freelist = obj;
		++size;
	}

	void PushRang(void* start, void* end, size_t n)
	{
		assert(start);
		assert(end);
		//头插
		NextObj(end) = _freelist;
		_freelist = start;
		size += n;
	}

	void* Pop()
	{
		assert(_freelist);
		//头删
		void* obj = _freelist;
		_freelist = NextObj(obj);
		--size;

		return obj;
	}

	void PopRang(void*& start, void*& end, size_t n)
	{
		assert(n <= size);
		start = _freelist;
		end = start;

		for (size_t i = 0; i < n - 1; ++i)
		{
			end = NextObj(end);
		}

		_freelist = NextObj(end);
		NextObj(end) = nullptr;
		size -= n;
	}

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

	size_t& MaxSize()
	{
		return _MaxSize;
	}

	size_t Size()
	{
		return size;
	}
private:
	void* _freelist = nullptr;
	size_t _MaxSize = 1;
	size_t size = 0;
};

class SizeClass
{
	// 整体控制在最多10%左右的内碎片浪费
	// [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)
private:
	自己想对齐的方法
	//static inline size_t _RoudUp(size_t size, size_t alignNum)
	//{
	//	if ((size % alignNum) != 0)
	//	{
	//		//如果size%上对齐数不等于0,则加上差的数,让size%上对齐数可以等于0
	//		size += (alignNum - (size % alignNum));
	//	}
	//	else
	//	{
	//		//如果size%对齐数刚好等于0.则直接返回
	//		return size;
	//	}

	//	return size;
	//}

	//别人的对齐方法
	static inline size_t _RoundUp(size_t bytes, size_t alignNum)
	{
		return ((bytes + alignNum - 1) & ~(alignNum - 1));
	}

	自己想对齐的方法
	//static inline size_t _index(size_t bytes, size_t alignNum)
	//{
	//	//字节数/对齐数 就是 该对象所在的桶,除了能被对齐数整除的除外
	//	if ((bytes % alignNum) == 0)
	//	{
	//		//例如: 8/8 = 1,但是8字节应该在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;
	}
public:
	static inline size_t RoundUp(size_t size)
	{

		if (size <= 128)
		{
			// [1,128]	8byte对齐
			return _RoundUp(size, 8);
		}
		else if (size <= 1024)
		{
			// [128+1,1024]	  16byte对齐
			return _RoundUp(size, 16);
		}
		else if (size <= 8 * 1024)
		{
	
			// [1024+1,8*1024]	128byte对齐
			return _RoundUp(size, 128);
		}
		else if (size <= 64 * 1024)
		{
			// [8*1024+1,64*1024]	1024byte对齐
			return _RoundUp(size, 1024);
		}
		else if (size <= 256 * 1024)
		{
			// [64*1024+1,256*1024]	8*1024byte对齐
			return _RoundUp(size, 8 * 1024);
		}
		else
		{
			return _RoundUp(size, 1 << PAGE_SHIFT);
		}
	}

	static inline size_t Index(size_t bytes)
	{
		assert(bytes <= MAX_BYTES);
		// 每个区间有多少个链
		static int group_array[4] = { 16, 56, 56, 56 };

		if (bytes <= 128)
		{
			// [1,128]					8byte对齐	    freelist[0,16)
			return _Index(bytes, 3);
		}
		else if (bytes <= 1024)
		{
			// [128+1,1024]				16byte对齐	    freelist[16,72)
			return _Index(bytes - 128, 4) + group_array[0];
		}
		else if (bytes <= 8 * 1024)
		{
			
			// [1024+1,8*1024]			128byte对齐	    freelist[72,128)
			return _Index(bytes - 1024, 7) + group_array[0] + group_array[1];
		}
		else if (bytes <= 64 * 1024)
		{
			// [8*1024+1,64*1024]		1024byte对齐     freelist[128,184)
			return _Index(bytes - 8 * 1024, 10) + group_array[0] + group_array[1] + group_array[2];
		}
		else if (bytes <= 256 * 1024)
		{
			// [64*1024+1,256*1024]		8*1024byte对齐   freelist[184,208)
			return _Index(bytes - 64 * 1024, 13) + group_array[0] + group_array[1] + group_array[2] + group_array[3];
		}
		else
		{
			assert(false);
			return -1;
		}
	}

	static size_t GetNumofSizeFromCentralCache(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;
	}

	// 计算一次向系统获取几个页
	// 单个对象 8byte
	// ...
	// 单个对象 256KB
	static size_t GetNumofPageFromPageCache(size_t size)
	{
		size_t num = GetNumofSizeFromCentralCache(size);
		size_t npage = num * size;

		npage >>= PAGE_SHIFT;
		if (npage == 0)
			npage = 1;

		return npage;
	}
};

// 管理多个连续页大块内存跨度结构
class Span
{
public:
	PAGE_ID _pageId = 0; // 大块内存起始页的页号
	size_t  _n = 0;      // 页的数量

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

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

	bool _isUse = false; //判断span是否正在被使用
	size_t objsize = 0;
};

class SpanList
{
public:
	SpanList()
	{
		_head = new Span;
		_head->_next = _head;
		_head->_prev = _head;
	}
	void PushFront(Span* span)
	{
		Insert(_head->_next, span);
	}
	void Insert(Span* pos,Span* newSpan)
	{
		assert(pos);
		assert(newSpan);
		Span* prev = pos->_prev;

		prev->_next = newSpan;
		newSpan->_prev = prev;

		newSpan->_next = pos;
		pos->_prev = newSpan;

		prev = nullptr;
	}
	Span* PopFront()
	{
		assert(_head != _head->_next);
		Span* front = _head->_next;
		Erase(_head->_next);
		return front;
	}
	void Erase(Span* pos)
	{
		assert(pos);
		assert(pos != _head);
		Span* prev = pos->_prev;
		Span* next = pos->_next;

		prev->_next = next;
		if(next)
			next->_prev = prev;

		pos = nullptr;
	}

	std::mutex& GetMutex()
	{
		return _mtx;
	}

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

	Span* End()
	{
		return _head;
	}

	bool Empty()
	{
		return _head == _head->_next;
	}
private:
	Span* _head;
	std::mutex _mtx;
};

ObjectPool.h

#pragma once
#include "Common.h"

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

		// 优先把还回来内存块对象,再次重复利用
		if (_freeList)
		{
			void* next = *((void**)_freeList);
			obj = (T*)_freeList;
			_freeList = next;
		}
		else
		{
			// 剩余内存不够一个对象大小时,则重新开大块空间
			if (_remainBytes < sizeof(T))
			{
				_remainBytes = 128 * 1024;
				//_memory = (char*)malloc(_remainBytes);
				_memory = (char*)SystemAlloc(_remainBytes >> 13);
				if (_memory == nullptr)
				{
					throw std::bad_alloc();
				}
			}

			obj = (T*)_memory;
			
			if (obj == nullptr)
			{
				int x = 0;
			}

			size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
			_memory += objSize;
			_remainBytes -= objSize;
		}

		
		// 定位new,显示调用T的构造函数初始化
		new(obj) T;
		

		return obj;
	}

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

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

private:
	char* _memory = nullptr; // 指向大块内存的指针
	size_t _remainBytes = 0; // 大块内存在切分过程中剩余字节数

	void* _freeList = nullptr; // 还回来过程中链接的自由链表的头指针
};

ThreadCache.h

#pragma once
#include "Common.h"

class ThreadCache
{
public:
	//申请和释放内存
	void* Allocate(size_t size);
	void Deallocate(void* ptr, size_t size);

	//从中心缓存获取
	void* FetchFromCentralCache(size_t index, size_t size);

	// 释放对象时,链表过长时,回收内存回到中心缓存
	void ListTooLong(FreeList& list, size_t size);
private:
	FreeList _freelists[TCFREELIST];
};

// TLS thread local storage
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;

ThreadCache.cpp

#include "ThreadCache.h"
#include "CentralCache.h"
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	//批量获取多个对象
	//SizeClass::GetNumofSizeFromCentralCache(size)    设置获取对象的上限值
	//_freelists[index].MaxSize()     该值一直会慢增长,直到达到上限值
	size_t batchNum = min(SizeClass::GetNumofSizeFromCentralCache(size),_freelists[index].MaxSize());

	if (batchNum == _freelists[index].MaxSize())
	{
		//慢增长控制,类似网络拥塞控制,指数方式增长
		_freelists[index].MaxSize() *= 2;
	}

	void* start = nullptr;
	void* end = nullptr;
	//以输出型参数形式传入参数
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(start);
	assert(end);
	assert(actualNum > 0);
	
	void* cur = start;
	size_t i = 0;
	while (cur != end)
	{
		cur = NextObj(cur);
		++i;
	}
	++i;
	if (i == actualNum)
	{
		int x = 0;
	}


	//actualNum实际获得的对象个数
	if (actualNum == 1)
	{
		assert(start == end);
		NextObj(start) = nullptr;
	}
	else
	{
		_freelists[index].PushRang(NextObj(start), end, actualNum - 1);
		NextObj(start) = nullptr;
	}

	return start;
}

void* ThreadCache::Allocate(size_t size)
{
	assert(size <= MAX_BYTES);
	//计算申请对象的对齐字节数和在哪个桶申请的桶下标
	size_t ObjSize = SizeClass::RoundUp(size);
	size_t ObjIndex = SizeClass::Index(size);
	void* obj = nullptr;

	//如果桶不为空,则直接从头部弹一个对象出去
	if (!_freelists[ObjIndex].Empty())
	{
		obj = _freelists[ObjIndex].Pop();
	}
	else//如果桶为空,则向中心缓存申请批量数据
	{
		obj = FetchFromCentralCache(ObjIndex, ObjSize);
	}

	return obj;
}
void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);

	//查找ptr所在的桶
	size_t ObjIndex = SizeClass::Index(size);

	//然后将对象插入该桶的自由链表
	_freelists[ObjIndex].Push(ptr);
	ptr = nullptr;

	//如果链表的对象个数 >= 一次批量申请对象的个数,则释放一些对象回CentralCache
	size_t n = min(SizeClass::GetNumofSizeFromCentralCache(size), _freelists[ObjIndex].MaxSize()) - 1;
	if (n == SizeClass::GetNumofSizeFromCentralCache(size) - 1)
	{
		n += 1;
	}
	//size_t n = 7;
	if (_freelists[ObjIndex].Size() >= n)
	{
		ListTooLong(_freelists[ObjIndex], size);
	}

}
void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	size_t n = min(SizeClass::GetNumofSizeFromCentralCache(size), list.MaxSize()) - 1;
	if (n == SizeClass::GetNumofSizeFromCentralCache(size) - 1)
	{
		n += 1;
	}
	//size_t n = 7;
	list.PopRang(start, end, n);

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

ConcurrentAlloc.h

#pragma once
#include "ThreadCache.h"
#include "PageCache.h"

void* ConcurrentAlloc(size_t size)
{
	if (size > MAX_BYTES)
	{
		size_t alignSize = SizeClass::RoundUp(size);
		size_t kpage = alignSize >> PAGE_SHIFT;
		
		PageCache::GetInstance()->GetMutex().lock();
		Span* span = PageCache::GetInstance()->NewSpan(kpage);
		span->_isUse = true;
		span->objsize = alignSize;
		PageCache::GetInstance()->GetMutex().unlock();

		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		return ptr;
	}
	else
	{
		assert(size <= MAX_BYTES);
		if (pTLSThreadCache == nullptr)
		{
			ObjectPool<ThreadCache> tcpool;
			//pTLSThreadCache = new ThreadCache;
			pTLSThreadCache = tcpool.New();
		}

		return pTLSThreadCache->Allocate(size);
	}
}
void ConcurrentFree(void* ptr)
{
	assert(ptr);
	Span* span = PageCache::GetInstance()->MapObjectToSpan(ptr);
	size_t size = span->objsize;

	if (size > MAX_BYTES)
	{
		

		PageCache::GetInstance()->GetMutex().lock();
		PageCache::GetInstance()->ReleaseSpanToPageCache(span);
		PageCache::GetInstance()->GetMutex().unlock();
	}
	else
	{
		assert(pTLSThreadCache);
		pTLSThreadCache->Deallocate(ptr, size);
	}

	
}

CentralCache.h

#pragma once
#include "Common.h"
class CentralCache
{
public:
	static CentralCache* GetInstance()
	{
		return &_Instance;
	}

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

	//从CentralCache批量获取一些对象
	size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size);

	//释放多个对象回span
	void ReleaseListToSpans(void* start, size_t size);
private:
	CentralCache()
	{}

	CentralCache(const CentralCache& _Instance) = delete;

private:
	SpanList _SpanFreeLists[CCFREELIST];
	static CentralCache _Instance;
};

CentralCache.cpp

#include "CentralCache.h"
#include "PageCache.h"
CentralCache CentralCache::_Instance;
Span* CentralCache::GetOneSpan(SpanList& Spanlist, size_t size)
{
	//查找Spanlist是否有span,如果有直接返回,如果没有一直往后找直到结束
	Span* it = Spanlist.Begin();
	while (it != Spanlist.End())
	{
		if (it->_freeList != nullptr)
		{
			return it;
		}
		else
		{
			it = it->_next;
		}
	}

	//都没有就找下一层PageCache要一个span
	Spanlist.GetMutex().unlock();

	PageCache::GetInstance()->GetMutex().lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::GetNumofPageFromPageCache(size));
	span->_isUse = true;
	span->objsize = size;
	PageCache::GetInstance()->GetMutex().unlock();

	assert(span);

	//切分span
	char* start = (char*)(span->_pageId << PAGE_SHIFT);

	//该span总的字节数大小
	size_t bytes = (span->_n << PAGE_SHIFT);
	char* end = start + bytes;

	span->_freeList = start;
	void* tail = start;
	start += size;

	
	while (start < end)
	{
		NextObj(tail) = start;
		tail = start;
		start += size;
	}
	NextObj(tail) = nullptr;

	Spanlist.GetMutex().lock();
	Spanlist.PushFront(span);
	return span;
}
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t Index = SizeClass::Index(size);
	_SpanFreeLists[Index].GetMutex().lock();

	Span* span = GetOneSpan(_SpanFreeLists[Index], size);
	assert(span);
	if (span->_freeList == nullptr)
	{
		int x = 0;
	}
	assert(span->_freeList);

	
	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;

	_SpanFreeLists[Index].GetMutex().unlock();

	return actualNum;
}

void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);
	_SpanFreeLists[index].GetMutex().lock();

	while (start)
	{
		void* next = NextObj(start);
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);

		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;

		if (span->_useCount == 0)
		{
			_SpanFreeLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;
			
			_SpanFreeLists[index].GetMutex().unlock();

			PageCache::GetInstance()->GetMutex().lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->GetMutex().unlock();

			_SpanFreeLists[index].GetMutex().lock();
		}

		start = next;
	}

	_SpanFreeLists[index].GetMutex().unlock();
}

PageMap.h

#pragma once
#include "Common.h"
#include "ObjectPool.h"

// Single-level array
template <int BITS>
class TCMalloc_PageMap1 {
private:
	static const int LENGTH = 1 << BITS;
	void** array_;

public:
	typedef uintptr_t Number;

	//explicit TCMalloc_PageMap1(void* (*allocator)(size_t)) {
	explicit TCMalloc_PageMap1() {
		//array_ = reinterpret_cast((*allocator)(sizeof(void*) << BITS));
		size_t size = sizeof(void*) << BITS;
		size_t alignSize = SizeClass::RoundUp(size);
		array_ = (void**)SystemAlloc(alignSize >> PAGE_SHIFT);
		memset(array_, 0, sizeof(void*) << BITS);
	}

	// Return the current value for KEY.  Returns NULL if not yet set,
	// or if k is out of range.
	void* get(Number k) const {
		if ((k >> BITS) > 0) {
			return NULL;
		}
		return array_[k];
	}

	// REQUIRES "k" is in range "[0,2^BITS-1]".
	// REQUIRES "k" has been ensured before.
	//
	// Sets the value 'v' for key 'k'.
	void set(Number k, void* v) {
		array_[k] = v;
	}
};

// Two-level radix tree
template <int BITS>
class TCMalloc_PageMap2 {
private:
	// Put 32 entries in the root and (2^BITS)/32 entries in each leaf.
	static const int ROOT_BITS = 5;
	static const int ROOT_LENGTH = 1 << ROOT_BITS;

	static const int LEAF_BITS = BITS - ROOT_BITS;
	static const int LEAF_LENGTH = 1 << LEAF_BITS;

	// Leaf node
	struct Leaf {
		void* values[LEAF_LENGTH];
	};

	Leaf* root_[ROOT_LENGTH];             // Pointers to 32 child nodes
	void* (*allocator_)(size_t);          // Memory allocator

public:
	typedef uintptr_t Number;

	//explicit TCMalloc_PageMap2(void* (*allocator)(size_t)) {
	explicit TCMalloc_PageMap2() {
		//allocator_ = allocator;
		memset(root_, 0, sizeof(root_));

		PreallocateMoreMemory();
	}

	void* get(Number k) const {
		const Number i1 = k >> LEAF_BITS;
		const Number i2 = k & (LEAF_LENGTH - 1);
		if ((k >> BITS) > 0 || root_[i1] == NULL) {
			return NULL;
		}
		return root_[i1]->values[i2];
	}

	void set(Number k, void* v) {
		const Number i1 = k >> LEAF_BITS;
		const Number i2 = k & (LEAF_LENGTH - 1);
		ASSERT(i1 < ROOT_LENGTH);
		root_[i1]->values[i2] = v;
	}

	bool Ensure(Number start, size_t n) {
		for (Number key = start; key <= start + n - 1;) {
			const Number i1 = key >> LEAF_BITS;

			// Check for overflow
			if (i1 >= ROOT_LENGTH)
				return false;

			// Make 2nd level node if necessary
			if (root_[i1] == NULL) {
				//Leaf* leaf = reinterpret_cast((*allocator_)(sizeof(Leaf)));
				//if (leaf == NULL) return false;
				static ObjectPool<Leaf>	leafPool;
				Leaf* leaf = (Leaf*)leafPool.New();

				memset(leaf, 0, sizeof(*leaf));
				root_[i1] = leaf;
			}

			// Advance key past whatever is covered by this leaf node
			key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
		}
		return true;
	}

	void PreallocateMoreMemory() {
		// Allocate enough to keep track of all possible pages
		Ensure(0, 1 << BITS);
	}
};

// Three-level radix tree
template <int BITS>
class TCMalloc_PageMap3 {
private:
	// How many bits should we consume at each interior level
	static const int INTERIOR_BITS = (BITS + 2) / 3; // Round-up
	static const int INTERIOR_LENGTH = 1 << INTERIOR_BITS;

	// How many bits should we consume at leaf level
	static const int LEAF_BITS = BITS - 2 * INTERIOR_BITS;
	static const int LEAF_LENGTH = 1 << LEAF_BITS;

	// Interior node
	struct Node {
		Node* ptrs[INTERIOR_LENGTH];
	};

	// Leaf node
	struct Leaf {
		void* values[LEAF_LENGTH];
	};

	Node* root_;                          // Root of radix tree
	void* (*allocator_)(size_t);          // Memory allocator

	Node* NewNode() {
		Node* result = reinterpret_cast<Node*>((*allocator_)(sizeof(Node)));
		if (result != NULL) {
			memset(result, 0, sizeof(*result));
		}
		return result;
	}

public:
	typedef uintptr_t Number;

	explicit TCMalloc_PageMap3(void* (*allocator)(size_t)) {
		allocator_ = allocator;
		root_ = NewNode();
	}

	void* get(Number k) const {
		const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
		const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH - 1);
		const Number i3 = k & (LEAF_LENGTH - 1);
		if ((k >> BITS) > 0 ||
			root_->ptrs[i1] == NULL || root_->ptrs[i1]->ptrs[i2] == NULL) {
			return NULL;
		}
		return reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3];
	}

	void set(Number k, void* v) {
		ASSERT(k >> BITS == 0);
		const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
		const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH - 1);
		const Number i3 = k & (LEAF_LENGTH - 1);
		reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3] = v;
	}

	bool Ensure(Number start, size_t n) {
		for (Number key = start; key <= start + n - 1;) {
			const Number i1 = key >> (LEAF_BITS + INTERIOR_BITS);
			const Number i2 = (key >> LEAF_BITS) & (INTERIOR_LENGTH - 1);

			// Check for overflow
			if (i1 >= INTERIOR_LENGTH || i2 >= INTERIOR_LENGTH)
				return false;

			// Make 2nd level node if necessary
			if (root_->ptrs[i1] == NULL) {
				Node* n = NewNode();
				if (n == NULL) return false;
				root_->ptrs[i1] = n;
			}

			// Make leaf node if necessary
			if (root_->ptrs[i1]->ptrs[i2] == NULL) {
				Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf)));
				if (leaf == NULL) return false;
				memset(leaf, 0, sizeof(*leaf));
				root_->ptrs[i1]->ptrs[i2] = reinterpret_cast<Node*>(leaf);
			}

			// Advance key past whatever is covered by this leaf node
			key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
		}
		return true;
	}

	void PreallocateMoreMemory() {
	}
};

PageCache.h

#pragma once
#include "Common.h"
#include "ObjectPool.h"
#include "PageMap.h"

class PageCache
{
public:
	static PageCache* GetInstance()
	{
		return &_Instance;
	}
	Span* NewSpan(size_t size);
	std::mutex& GetMutex()
	{
		return _mtx;
	}

	//用PAGE_ID映射span对象指针
	Span* MapObjectToSpan(void* obj);

	// 释放空闲span回到Pagecache,并合并相邻的span
	void ReleaseSpanToPageCache(Span* span);
private:
	PageCache()
	{}

	PageCache(const PageCache& _Instance) = delete;

private:
	SpanList _SpanLists[PCSPANLIST];
	static PageCache _Instance;
	std::mutex _mtx;
	//std::unordered_map _idSpanMap;
	TCMalloc_PageMap1<32 - PAGE_SHIFT> _idSpanMap;
	ObjectPool<Span>  _spanPool;
};

PageCache.cpp

#include "PageCache.h"
PageCache PageCache::_Instance;
//获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);

	//申请大于128页的内存
	if (k > PCSPANLIST - 1)
	{
		void* ptr = SystemAlloc(k);

		//Span* kspan = new Span;
		Span* kspan = _spanPool.New();
		kspan->_pageId = ((PAGE_ID)ptr >> PAGE_SHIFT);
		kspan->_n = k;
		
		//_idSpanMap[kspan->_pageId] = kspan;
		_idSpanMap.set(kspan->_pageId, kspan);
		return kspan;

	}
	//先查找一下k处桶是否有span
	if (!_SpanLists[k].Empty())
	{
		Span* span = _SpanLists[k].PopFront();

		for (size_t i = 0; i < k; i++)
		{
			//_idSpanMap[span->_pageId + i] = span;
			_idSpanMap.set(span->_pageId + i, span);
		}
		return span;
	}

	//如果没有则往后面的桶查找
	for (size_t i = k + 1; i < PCSPANLIST; i++)
	{
		//如果后面的桶有则进行切分
		if (!_SpanLists[i].Empty())
		{
			Span* nspan = _SpanLists[i].PopFront();
			//Span* kspan = new Span;
			Span* kspan = _spanPool.New();
			// 在nSpan的头部切一个k页下来
			// k页span返回
			// nSpan再挂到对应映射的位置
			kspan->_pageId = nspan->_pageId;
			kspan->_n = k;
			for (size_t i = 0; i < k; i++)
			{
				//_idSpanMap[kspan->_pageId + i] = kspan;
				_idSpanMap.set(kspan->_pageId + i, kspan);
			}
			
			nspan->_pageId = nspan->_pageId + k;
			nspan->_n = nspan->_n - k;
			//_idSpanMap[nspan->_pageId] = nspan;
			_idSpanMap.set(nspan->_pageId, nspan);
			//_idSpanMap[nspan->_pageId +  nspan->_n - 1] = nspan;
			_idSpanMap.set(nspan->_pageId + nspan->_n - 1, nspan);
			_SpanLists[nspan->_n].PushFront(nspan);

			return kspan;
		}
	}

	//如果后面的桶也没有则找系统申请一个128页的span
	//Span* span = new Span;
	Span* span = _spanPool.New();
	void* ptr = SystemAlloc(PCSPANLIST - 1);
	span->_pageId = ((PAGE_ID)ptr >> PAGE_SHIFT);
	span->_n = PCSPANLIST - 1;
	_SpanLists[span->_n].PushFront(span);

	return NewSpan(k);
}

Span* PageCache::MapObjectToSpan(void* obj)
{
	PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);

	/*std::unique_lock lock(_mtx);*/

	/*auto ret = _idSpanMap.find(id);*/

	/*if (ret == _idSpanMap.end())
	{
		assert(false);
		return nullptr;
	}
	else
	{
		return ret;
	}*/

	auto ret = (Span*)_idSpanMap.get(id);
	assert(ret);

	return ret;

}
void PageCache::ReleaseSpanToPageCache(Span* span)
{
	// 大于128 page的直接还给堆
	if (span->_n > PCSPANLIST - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);

		//delete span;
		_spanPool.Delete(span);

		return;
	}

	//向前合并
	while (1)
	{
		PAGE_ID PrevSpanId = span->_pageId - 1;
		//auto ret = _idSpanMap.find(PrevSpanId);
		auto ret = (Span*)_idSpanMap.get(PrevSpanId);
		if (ret == nullptr)
		{
			break;
		}

		//Span* PrevSpan = ret->second;
		Span* PrevSpan = ret;
		if (PrevSpan->_isUse)
		{
			break;
		}

		if (PrevSpan->_n + span->_n > PCSPANLIST - 1)
		{
			break;
		}

		span->_pageId = PrevSpan->_pageId;
		span->_n += PrevSpan->_n;
		_SpanLists[PrevSpan->_n].Erase(PrevSpan);

		//delete PrevSpan;
		_spanPool.Delete(PrevSpan);
	}

	//向后合并
	while (1)
	{
		PAGE_ID NextSpanId = span->_pageId + span->_n;
		//auto ret = _idSpanMap.find(NextSpanId);
		auto ret = (Span*)_idSpanMap.get(NextSpanId);
		if (ret == nullptr)
		{
			break;
		}

		//Span* NextSpan = ret->second;
		Span* NextSpan = ret;
		if (NextSpan->_isUse)
		{
			break;
		}

		if (NextSpan->_n + span->_n > PCSPANLIST - 1)
		{
			break;
		}

		span->_n += NextSpan->_n;
		_SpanLists[NextSpan->_n].Erase(NextSpan);

		//delete NextSpan;
		_spanPool.Delete(NextSpan);
	}

	_SpanLists[span->_n].PushFront(span);
	span->_isUse = false;
	//_idSpanMap[span->_pageId] = span;
	_idSpanMap.set(span->_pageId, span);
	//_idSpanMap[span->_pageId + span->_n - 1] = span;
	_idSpanMap.set(span->_pageId + span->_n - 1, span);
}

测试代码BenchMark.cpp

#include "ConcurrentAlloc.h"

// ntimes 一轮申请和释放内存的次数
// rounds 轮次
void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
	std::vector<std::thread> vthread(nworks);
	std::atomic<size_t> malloc_costtime = 0;
	std::atomic<size_t> free_costtime = 0;

	for (size_t k = 0; k < nworks; ++k)
	{
		vthread[k] = std::thread([&, k]() {
			std::vector<void*> v;
			v.reserve(ntimes);

			for (size_t j = 0; j < rounds; ++j)
			{
				size_t begin1 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					v.push_back(malloc(16));
					//v.push_back(malloc((16 + i) % 8192 + 1));
				}
				size_t end1 = clock();

				size_t begin2 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					free(v[i]);
				}
				size_t end2 = clock();
				v.clear();

				malloc_costtime += (end1 - begin1);
				free_costtime += (end2 - begin2);
			}
			});
	}

	for (auto& t : vthread)
	{
		t.join();
	}

	printf("%u 个线程并发执行 % u轮次,每轮次malloc % u次: ",nworks, rounds, ntimes);
	cout << "花费:" << malloc_costtime  << "ms"<< endl;

	printf("%u个线程并发执行%u轮次,每轮次free %u次:",nworks, rounds, ntimes);
	cout << "花费:" << free_costtime << "ms" << endl;

	printf("%u个线程并发malloc&free %u次",nworks, nworks * rounds * ntimes);
	cout << "总计花费:" << malloc_costtime + free_costtime << "ms" << endl;

}


// 单轮次申请释放次数 线程数 轮次
void BenchmarkConcurrentMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
	std::vector<std::thread> vthread(nworks);
	std::atomic<size_t> malloc_costtime = 0;
	std::atomic<size_t> free_costtime = 0;

	for (size_t k = 0; k < nworks; ++k)
	{
		vthread[k] = std::thread([&]() {
			std::vector<void*> v;
			v.reserve(ntimes);

			for (size_t j = 0; j < rounds; ++j)
			{
				/*if (j == 1)
				{
					int x = 0;
				}*/
				size_t begin1 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					v.push_back(ConcurrentAlloc(16));
					//v.push_back(ConcurrentAlloc((16 + i) % 8192 + 1));
				}
				size_t end1 = clock();

				size_t begin2 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					ConcurrentFree(v[i]);
				}
				size_t end2 = clock();
				v.clear();

				malloc_costtime += (end1 - begin1);
				free_costtime += (end2 - begin2);
			}
			});
	}

	for (auto& t : vthread)
	{
		t.join();
	}

	printf("%u 个线程并发执行 % u轮次,每轮次ConcurrentMalloc % u次: ", nworks, rounds, ntimes);
	cout << "花费:" << malloc_costtime << "ms" << endl;

	printf("%u个线程并发执行%u轮次,每轮次Concurrentfree %u次:", nworks, rounds, ntimes);
	cout << "花费:" << free_costtime << "ms" << endl;

	printf("%u个线程并发ConcurrentMalloc&Concurrentfree %u次", nworks, nworks * rounds * ntimes);
	cout << "总计花费:" << malloc_costtime + free_costtime << "ms" << endl;
}

int main()
{
	size_t n = 10000;
	cout << "==========================================================" << endl;
	BenchmarkConcurrentMalloc(n, 4, 10);
	cout << endl << endl;

	BenchmarkMalloc(n, 4, 10);
	cout << "==========================================================" << endl;

	return 0;
}

你可能感兴趣的:(项目高并发内存池,C++,C,高并发内存池,多线程,数据结构,OS)