C++项目高并发内存池_ThreadCache设计

thread cache设计思路

thread cache:每一个线程都有一个独立的空间,当线程都要申请空间(小于256KB)时直接在线程内部申请,不需要加锁竞争。

根据之前设计的定长内存池可知,一条链表管理释放内存只能处理固定内存大小的情况
如:这个自由链表每个节点都是4字节,这条链表只能处理申请4字节空间的情况。
而实际线程申请的空间不一定是相同的所以这里需要多条链表来处理。多条链表用哈希桶来管理

但是哈希桶中桶个数为多少合适呢,假设以8字节为一个桶

在这里插入图片描述

这里选择每8字节为一个桶,如果要申请5字节内存,也向其提供8字节。
申请9字节提供16字节空间。(这样不可避免的造成了内存泄漏的问题)
这里我们单独设计类管理对齐规则。

因为在64位下一个指针为8字节,所以每个桶中链表节点大小至少要等于8字节

但是每8个字节一个桶,桶的个数太多了。所以采用分级分桶

整体控制在最多10%左右的内碎片浪费
[1,128] 8byte对齐 freelist[0,16)
[128+1,1KB] 16byte对齐 freelist[16,72)
[1KB+1,8KB] 128byte对齐 freelist[72,128)
[8KB+1,64KB] 1KB对齐 freelist[128,184)
[64KB+1,256KB] 8KB对齐 freelist[184,208)
这样的话256KB只需要208个桶(208个自由链表)可以接受。

#pragma once

#include<iostream>
#include<vector>
#include<time.h>
#include<assert.h>

using std::cout; using std::endl;

static const size_t MAX_BYTE = 126 * 1024;//如果线程申请超过126KB不能直接向ThreadCache申请空间

static const size_t NUMLIST = 208;//ThreadCache中哈希桶的个数

//获取自由链表下一个节点
void*& NextObj(void* obj) {
	return *(void**)obj;
}

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

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

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

//计算ThreadCache中哈希桶的桶个数
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{
			assert(false);
			return -1;
		}
	}
	//计算在哈希桶的那个位置
	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;
		}
	}
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;
	}
};

thread cache实现无锁访问(线程本地存储)

线程共享进程地址空间,每个线程有独立的栈。高并发内存池中每个线程都有独立的thread cache。

线程本地存储:是一种变量的储存方法,这个变量在线程内部是可访问的,但是其他线程不能访问到。(TLS)

Windows TLS

Linux TLS

window创建静态ThreadCacheTLS为static __declspec(thread) ThreadCache*

还要注意用static修饰,防止重复定义

C++高并发内存池 ThreadCache代码

1.公共头文件Commen.h

#pragma once

#include<iostream>
#include<vector>
#include<time.h>
#include<assert.h>
#include<thread>

using std::cout; using std::endl;

static const size_t MAX_BYTE = 126 * 1024;//如果线程申请超过126KB不能直接向ThreadCache申请空间

static const size_t NUMLIST = 208;//ThreadCache中哈希桶的个数

//获取自由链表下一个节点
void*& NextObj(void* obj) {
	return *(void**)obj;
}

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

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

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

//计算ThreadCache中哈希桶的桶个数
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{
			assert(false);
			return -1;
		}
	}
	//计算在哈希桶的那个位置
	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;
		}
	}
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;
	}
};

2.ThreadCache.h

#pragma once

#include"Common.h"

class ThreadCache {
private:
	FreeList _FreeList[NUMLIST];
public:
	//申请释放空间
	void* ApplySpace(size_t size) {
		assert(size <= MAX_BYTE);
		size_t AligSize = SizeClass::RoundUp(size);
		//计算桶位置
		size_t index = SizeClass::Index(AligSize);
		if (!_FreeList[index].Empty()) {
			return _FreeList[index].Pop();
		}
		else {
			//thread cache没有内存向central cache要空间
			return RequestFromCentralCache(index, AligSize);
		}
	}
	void ReleaseSpace(void* ptr, size_t size) {
		assert(size <= MAX_BYTE && ptr != nullptr);
		size_t index = SizeClass::Index(size);//找到第几个桶
		//将这个空间头插到自由链表上
		_FreeList[index].Push(ptr);
	}

	//向中心缓存申请空间,先空开
	void* RequestFromCentralCache(size_t index, size_t size) { return nullptr; }
};

//每个线程都会拥有自己的ThreadCache
static __declspec(thread) ThreadCache* tls_threadcache = nullptr;//用static修饰只在当前文件可见

3.线程申请独立的ThreadCache空间实现无锁(ConcurrentAlloc.h)

#pragma once

#include"Common.h"
#include"ThreadCache.h"

//线程调用申请ThreadCache空间
static void* ConcurrentAlloc(size_t size) {
	//获取线程自己的ThreadCache
	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) {
	//释放时每个线程一定有tls_threadcache
	assert(tls_threadcache != nullptr);
	tls_threadcache->ReleaseSpace(ptr,size);
}

测试Window下静态TLS

#include"ConcurrentAlloc.h"

void Alloc() {
	for (int i = 0; i < 10; i++) {
		void* ptr = ConcurrentAlloc(6);
	}
}

void Alloc2() {
	for (int i = 0; i < 10; i++) {
		void* ptr = ConcurrentAlloc(7);
	}
}

void TestTLS() {
	std::thread t1(Alloc);
	t1.join();
	std::thread t2(Alloc2);
	t2.join();
}

int main()
{
	TestTLS();
	return 0;
}

在这里插入图片描述
如上图可知两个线程拥有不同的ThreadCache对象,且这两个对象相互独立,这两个线程申请ThreadCache空间时不需要加锁

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NUC_Dodamce

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值