定长内存池设计ConcurrentMemoryPool

news/2024/11/8 3:14:57/

原理

在这里插入图片描述
还回来的内存用链表串联起来,称为自由链表
内存块自身进行链接,前四个字节存下一个的地址
在这里插入图片描述

结构

template<class T>
class ObjectPool
{
public:T* New(){}
private:char* _memory = nullptr;  //方便切割void* _freeList = nullptr;
};

第一步:先给_memory搞一块大内存

	T* New(){if (_remainBytes <sizeof(T))  //剩余的内存不够分配一个T的大小,重开一个大空间{_remainBytes = 128 * 1024;_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byteif (_memory == nullptr){throw bad_alloc();}}T* obj = (T*)_memory;_memory += sizeof(T);_remainBytes -= sizeof(T);return obj;}

考虑对象的还回

前四/八个字节空间用来指向
在这里插入图片描述
在这里插入图片描述
采用头插的方式
在这里插入图片描述

new从freeList中优先切出

在这里插入图片描述

T* New(){T* obj = nullptr;//优先利用freeList中的if (_freeList){void* next = *((void**)_freeList);obj =(T*)_freeList;  //obj指向第一块_freeList = next;return obj;}else{if (_remainBytes < sizeof(T))  //剩余的内存不够分配一个T的大小,重开一个大空间{_remainBytes = 128 * 1024;_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byteif (_memory == nullptr){throw bad_alloc();}}obj = (T*)_memory;_memory += sizeof(T);_remainBytes -= sizeof(T);return obj;}}

平台冲突问题,申请值小于一个指针大小(64位平台,指针大小为8byte),则给一个指针。并且保证自由链表在链接时,空间至少能存进下一个对象的地址。

加上obj初始化,显示调用析构函数清理对象(T)

#pragma once
#include <iostream>
using std::cout;
using std::endl;
using std::bad_alloc;
//定长内存池
//template<size_t N>
//class ObjectPool
//{};
template<class T>
class ObjectPool
{
public:T* New(){T* obj = nullptr;//优先利用freeList中的if (_freeList){void* next = *((void**)_freeList);obj =(T*)_freeList;  //obj指向第一块_freeList = next;}else{if (_remainBytes < sizeof(T))  //剩余的内存不够分配一个T的大小,重开一个大空间{_remainBytes = 128 * 1024;_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byteif (_memory == nullptr){throw bad_alloc();}}obj = (T*)_memory;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){//显示调用析构函数清理对象(T)obj->~T();//头插*(void**)obj = _freeList;_freeList = obj;} private:char* _memory = nullptr;  //方便切割  指向大块内存的指针size_t _remainBytes = 0;  //大块内存在切分过程中剩余字节数void* _freeList = nullptr;//还回来的内存链接到的自由链表的头指针
};

测试性能

用数结构分别对比vector容器下(malloc)std::vector<TreeNode*>和此内存池下ObjectPool的运行时间
ConcurrentMemoryPoll.cpp

// ConcurrentMemoryPool.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include "ObjectPool.h"int main()
{TestObjectPool();return 0;
}

ObjectPool.h

#pragma once
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::bad_alloc;
//定长内存池
//template<size_t N>
//class ObjectPool
//{};
template<class T>
class ObjectPool
{
public:T* New(){T* obj = nullptr;//优先利用freeList中的if (_freeList){void* next = *((void**)_freeList);obj =(T*)_freeList;  //obj指向第一块_freeList = next;}else{if (_remainBytes < sizeof(T))  //剩余的内存不够分配一个T的大小,重开一个大空间{_remainBytes = 128 * 1024;_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byteif (_memory == nullptr){throw bad_alloc();}}obj = (T*)_memory;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){//显示调用析构函数清理对象(T)obj->~T();//头插*(void**)obj = _freeList;_freeList = obj;} private:char* _memory = nullptr;  //方便切割  指向大块内存的指针size_t _remainBytes = 0;  //大块内存在切分过程中剩余字节数void* _freeList = nullptr;//还回来的内存链接到的自由链表的头指针
};struct TreeNode
{int _val;TreeNode* _left;TreeNode* _right;TreeNode():_val(0), _left(nullptr), _right(nullptr){}
};void TestObjectPool()
{// 申请释放的轮次const size_t Rounds = 5;// 每轮申请释放多少次const size_t N = 100000;std::vector<TreeNode*> v1;v1.reserve(N);size_t begin1 = clock();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();std::vector<TreeNode*> v2;v2.reserve(N);ObjectPool<TreeNode> TNPool;size_t begin2 = clock();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;
}

Debug版本
32位
在这里插入图片描述
64位
在这里插入图片描述

Release版本
32位
在这里插入图片描述
64位
在这里插入图片描述
清晰看出,ConcurrentMemoryPool的高效


http://www.ppmy.cn/news/1031561.html

相关文章

.NET 6.0 重启 IIS 进程池

在 .NET 6.0 中&#xff0c;你可以使用 Microsoft.Web.Administration 命名空间提供的 API 来管理 IIS 进程池并实现重启操作。以下是一个示例代码&#xff0c;展示如何使用 .NET 6.0 中的 Microsoft.Web.Administration 来重启 IIS 进程池&#xff1a; using Microsoft.Web.A…

高并发内存池项目(C++实战项目)

项目介绍 项目来源 本项目实现了一个高并发内存池&#xff0c;参考了Google的开源项目tcmalloc实现的简易版&#xff1b;其功能就是实现高效的多线程内存管理。由功能可知&#xff0c;高并发指的是高效的多线程&#xff0c;而内存池则是实现内存管理的。 tcmalloc源码 项目…

【高频面试题】常见技术场景

文章目录 单点登录这块怎么实现的权限认证是如何实现的上传数据的安全性怎么控制&#xff1f;你们项目中日志怎么采集的查看日志的命令生产问题怎么排查怎么快速定位系统的瓶颈 单点登录这块怎么实现的 单点登录的英文名叫做&#xff1a;Single Sign On&#xff08;简称SSO&am…

设计师常用的UI设计软件推荐

如今&#xff0c;随着互联网时代设计岗位的演变&#xff0c;近年来出现了一位新兴而受欢迎的专业UI设计师。对于许多对UI设计感兴趣或刚刚接触UI设计的初学者来说&#xff0c;他们不禁想知道&#xff0c;成为一名优秀的UI设计师需要掌握哪些UI软件&#xff1f;今天&#xff0c;…

Python爬虫——selenium的安装和基本使用

1.什么是selenium&#xff1f; selenium是一个用于web应用程序测试的工具selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样支持通过各种driver&#xff08;FrifoxDriver&#xff0c;ItenrentExploreDriver&#xff0c;OperaDriver&#xff0c;ChromeDrive…

CF 1859E Maximum Monogonosity 绝对值的巧妙处理和状态设计

CF 1859E 题意:给你两个长度为 n n n的数组 a a a, b b b&#xff0c;选择一些点对 ( l i , r i ) (l_i,r_i) (li​,ri​)。 每个点对对答案产生贡献 a b s ( b l i − a r i ) a b s ( b r i − a l i ) abs(b_{l_i}-a_{r_i})abs(b_{r_i}-a_{l_i}) abs(bli​​−ari​​)abs(…

配置网络设置和修改主机名

bash 题目&#xff1a; 在 node1 上配置网络&#xff0c;要求如下&#xff1a; 主机名&#xff1a;node1.domain8.rhce.cc IP地址: 172.25.250.10/24 ##注意掩码 网关&#xff1a; 172.25.250.250 DNS&#xff1a; 172.25.250.250 ##名称服务器 做法&#xff1a; nmtui 回车…

go内存管理机制

golang内存管理基本是参考tcmalloc来进行的。go内存管理本质上是一个内存池&#xff0c;只不过内部做了很多优化&#xff1a;自动伸缩内存池大小&#xff0c;合理切割内存块。 基本概念&#xff1a; Page&#xff1a;页&#xff0c;一块 8 K大小的内存空间。Go向操作系统申请和…