当前位置: 首页 > news >正文

定长池的实现

目录

一、定长池的框架      

二、如何脱离malloc的内存池,直接从堆拿空间?

三、如何设计内存块的指针?

四、代码框架及实现

五、性能测试


一、定长池的框架      

       在学习高并发内存池之前,我们先来实现一个定长池,他对于我们后面的内存池具有启发意义。定长池也叫对象池,只能存储一个类型的对象,因为该类型的对象大小肯定也是一样的,所有也叫定长池。

        作为程序员(C/C++)我们知道申请内存使用的是malloc,malloc其实就是一个通用的大众货,什么场景下都可以用,但是什么场景下都可以用就意味着什么场景下都不会有很高的性能,下面我们就先来设计一个定长内存池做个开胃菜,当然这个定长内存池在我们后面的高并发内存池中也是有价值的,所以学习他目的有两层,先熟悉一下简单内存池是如何控制的,第二他会作为我们后面内存池的一个基础组件。

 上面这一幅图就是不同的场景需要使用不同的对象,我们用不同的内存池俩存储对象们,而非使用malloc,这样就减少了内存碎片,所谓术业有专攻正是如此。

        当我们申请空间的时候,直接从堆中申请一大块内存,由我们自己维护,如果要释放一个个的小内存块,则是把内存块挂起到_freelist中,这样以后再需要申请内存块则直接从_freelist中拿,不用再找系统的堆中拿了,从而提高了效率。

二、如何脱离malloc的内存池,直接从堆拿空间?

        我们知道malloc的底层实际上是调用了brk和mmap等系统调用的(在linux情况下),而且malloc底层是自己维护了一个通用内存池,那么我们只需要跳过malloc,直接调用系统调用就能脱离malloc的内存池了。

        在Windows环境中,我们使用的是和brk类似的接口。

//参数:要k页,一页是4096个字节
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
	void* ptr = VirtualAlloc(0, kpage * (1 << 12), MEM_COMMIT | MEM_RESERVE,
		PAGE_READWRITE);
#else
	// linux下brk mmap等
#endif
	if (ptr == nullptr)
		throw std::bad_alloc();
	return ptr;
}

三、如何设计内存块的指针?

        在学习链表的时候,我们通常会把链表的节点设计成两个部分,一个存放下一个节点的地址的指针,剩下一个才是存放数据的空间。

        但是在这里,我们并不采取上述做法,因为每一个链表节点都要存储指针的话,会让链表节点变大,由于我们上层应用并不需要遍历链表等操作,所以我们直接让链表指针使用内存块结构的前4/8个字节(取决于你的操作系统是32还是64位的),在上层申请空间的时候,返回该点。        

        这样该节点的所有空间都能用于存储数据,而delete之后,由于数据不在被需要,可以直接覆盖式的在前4/8个字节写上下一个内存块节点的指针。

所以如何获取一块空间的前4/8个字节呢?

四、代码框架及实现

(1)当自由链表有空间的时候,优先从自由链表中拿。

(2)当自由链表没有空间的时候,从memory中拿。

(3)当memory中没有空间的时候,才需要调用系统调用从堆中拿空间。

(4)如果一个对象的大小小于指针大小,则无法存储下一个节点的地址,所以一个节点最小4/8个字节。

template<class T>
class ObjectPool
{
public:
	T* New();
	void Delete(T* obj);

private:
	char* _memory=nullptr;
	void* _freelist=nullptr;
	size_t _remainByte=0;
};
template<class T>
T* ObjectPool<T>::New()
{
	T* obj = nullptr;

	//1.从freelist中拿
	if (_freelist!=nullptr)
	{
		void* next = *((void**)_freelist);
		obj = (T*)_freelist;
		_freelist = next;
	}
	else
	{
		//3.如果memory没有/不够,从系统调用brk拿
		if (_remainByte < sizeof(T))
		{
			_remainByte = 128 * 1024;
			_memory = (char*)SystemAlloc(_remainByte>>12);
			//_memory = (char*)malloc(128 * 1024);
			if (_memory == nullptr)
			{
				throw std::bad_alloc();
			}
		}
		//2.如果freelist没有,从memory拿
		obj = (T*)_memory;
		size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
		_memory += objSize;
		_remainByte -= objSize;
	}
	//初始化对象
	//定位new,显式调用构造函数
	new(obj)T;
	return obj;
}

template<class T>
void ObjectPool<T>::Delete(T* obj)
{
	//显式调用析构函数
	obj->~T();
    //头插不用考虑是否为空链表
	*(void**)obj = _freelist;
	_freelist = obj;
}

五、性能测试

        在这里我们构建了一个树型节点,当做对象。每一次申请释放该对象N次,一个使用c++的关键字new,另外一个使用我们ObjectPool中的new()成员函数,观察运行时间。

struct TreeNode
{
	int _val;
	TreeNode* _left;
	TreeNode* _right;
	TreeNode()
		:_val(0)
		, _left(nullptr)
		, _right(nullptr)
	{
	}
};
void TestObjectPool()
{
	// 申请释放的轮次
	const size_t Rounds = 3;
	// 每轮申请释放多少次
	const size_t N = 1000000;
	size_t begin1 = clock();
	std::vector<TreeNode*> v1;
	v1.reserve(N);
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (int i = 0; i < N; ++i)
		{
			v1.push_back(new TreeNode);
		}
		for (int i = 0; i < N; ++i)
		{
			delete v1[i];
		}
		v1.clear();
	}
	size_t end1 = clock();
	ObjectPool<TreeNode> TNPool;
	size_t begin2 = clock();
	std::vector<TreeNode*> v2;
	v2.reserve(N);
	for (size_t j = 0; j < Rounds; ++j) 
	{
		for (int i = 0; i < N; ++i)
		{
			v2.push_back(TNPool.New());
		}
		for (int i = 0; i < N; ++i)
		{
			TNPool.Delete(v2[i]);
		}
		v2.clear();
	}
	size_t end2 = clock();
	cout << "new cost time:" << end1 - begin1 << endl;
	cout << "object pool cost time:" << end2 - begin2 << endl;
}

可以看到,效率明显比直接使用new关键字要高许多。

http://www.dtcms.com/a/112250.html

相关文章:

  • 蓝桥杯 小明的背包1 小兰的神秘礼物 01背包问题 模板 C++
  • 财务税务域——企业税务系统设计
  • centos8上实现lvs集群负载均衡dr模式
  • 【学Rust写CAD】23 渐变效果(gradient_source.rs)
  • 【面试篇】Dubbo
  • NSSCTF [HGAME 2023 week1]simple_shellcode
  • 音视频入门基础:MPEG2-PS专题(8)——使用Wireshark分析GB28181的PS流
  • 第十二步:react
  • 如何用Python轻松实现快速复制或剪切文件列表中的所有文件呢?
  • 【架构艺术】Go大仓monorepo中使用wire做依赖注入的经验
  • PowerMonitor的使用步骤
  • 【jvm】GC评估指标
  • 面试手撕------智能指针
  • 操作系统、虚拟化技术与云原生及云原生AI简述
  • JavaScript智能对话机器人——企业知识库自动化
  • 使用HTML5和CSS3实现炫酷的3D立方体动画
  • 地球科学领域常用python库
  • 软件工程面试题(十九)
  • Redis高级技能进阶
  • 【GPT写代码】动作视频切截图研究器
  • MATLAB中plot函数的详细参数表
  • 数据结构:二叉树(三)·(重点)
  • 医疗信息系统的主要痛点分析
  • session临时文件包含
  • 【教学类-102-02】自制剪纸图案(留白边、沿线剪)02——Python+PS自动化添加虚线边框
  • ROS订阅相机图像识别颜色并发布识别信息
  • 【进收藏夹吃灰】Python基础学习指南
  • 【读书笔记·VLSI电路设计方法解密】问题61:扫描插入的目的是什么
  • java 局部内部类
  • Git 教程:从 0 到 1 全面指南 教程【全文三万字保姆级详细讲解】