高并发内存池(六):补充内容

目录

有关大于256KB内存的申请和释放处理方法 

处理大于256KB的内存申请

补充内容1

补充内容2

补充内容3 

处理大于256KB的内存释放

新增内容1

新增内容2

测试函数

使用定长内存池替代new

释放对象时不传对象大小

补充内容1

补充内容2

 补充内容3

补充内容4

测试函数

为哈希桶加锁

多线程环境下对比malloc测试

复杂问题的调试技巧

性能瓶颈分析

针对性能瓶颈使用基数树进行优化

基数树代码


有关大于256KB内存的申请和释放处理方法 

内存申请和释放大于256KB也分为两种情况:

  1. 大于256KB但是小于128 * 8 * 1024KB
  2. 大于128 * 8 * 1024KB

内存申请大于256KB但小于128 * 8 * 1024KB:因为PageCache中存放的span所管理的最大页数为128,即span可分配的最大内存为128 * 8 * 1024 KB,所以当可以直接向PageCache申请一个合适的span(该span管理的页数为此次内存申请对齐后大小 / 页大小)

内存申请大于128 * 8 *1024 KB:PageCache中已经没有合适的span了,直接向堆上申请

处理大于256KB的内存申请

补充内容1

在RoundUp函数中新增用于处理大于256KB内存申请的内存对齐判断

(_RoundUp函数不变)

//用于内存对齐
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{return _RoundUp(size, 1 << PAGE_SHIFT);//对齐数为当前规定的页的大小}
}

补充内容2

在NewSpan开始处中新增获取管理页数大于128的span的判断

//所需页数大于128PageCache无法分配,需要向堆申请
if (k > NPAGES - 1)
{void* ptr = SystemAlloc(k);//从堆获取一块内存Span* span = new Span;//申请一个span结点span->_PageId = (size_t)ptr >> PAGE_SHIFT;//新span中的页号为ptr指向的内存地址 / 页大小span->_n = k;//新span中的页数为n个_idSpanMap[span->_PageId] = span;//存放映射关系,即使该span不会被挂在PageCache上return span;
}

补充内容3 

在ConcurrentAlloc函数新增size > MAX_BYTES的判断

//申请内存
static void* ConcurrentAlloc(size_t size)
{if (size > MAX_BYTES){size_t alignSize = SizeClass::RoundUp(size);//内存对齐size_t kpage = alignSize >> PAGE_SHIFT;//计算所需页数PageCache::GetInstance()->_pageMtx.lock();Span* span = PageCache::GetInstance()->NewSpan(kpage);//向PageCache申请管理kpage个页的span,若kpage > 128则需要经过补充内容2中的内容,否则还是按照原NewSpan执行void* ptr = (void*)(span->_PageId << PAGE_SHIFT);//通过页号计算地址PageCache::GetInstance()->_pageMtx.unlock();return ptr;//返回从PageCache分配给的内存空间的地址}else{...//获取TLS的那部分内容}
}

处理大于256KB的内存释放

新增内容1

在ReleaseSpanToPageCache开始处新增当归还的span的_n > 128的判断

//大于128页的span直接还给堆
if (span->_n > NPAGES - 1)
{void* ptr = (void*)(span->_PageId << PAGE_SHIFT);SystemFree(ptr);delete span;return; 
}

新增内容2

在ConcurrentFree中新增用于size > MAX_BYTES的判断

//释放内存
static void ConcurrentFree(void* ptr,size_t size)
{if (size > MAX_BYTES){Span* span = PageCache::GetInstance()->MapObjectToSpan(ptr);//根据归还的内存地址获取要归还的spanPageCache::GetInstance()->_pageMtx.lock();PageCache::GetInstance()->ReleaseSpanToPageCache(span);//将要归还的span挂在PageCache上,或者返还给堆PageCache::GetInstance()->_pageMtx.unlock();}else{...//还是原来的那两行释放内容}
}

测试函数

//用于测试内存申请和释放大于256KB
void BigAlloc()
{//内存申请和释放大于256KB,但是小于128 * 8 * 1024KBvoid* p1 = ConcurrentAlloc(257 * 1024);ConcurrentFree(p1, 257 * 1024);//内存申请和释放大于128 * 8 * 1024KBvoid* p2 = ConcurrentAlloc(129 * 8 * 1024);ConcurrentFree(p2, 129 * 8 * 1024);
}

使用定长内存池替代new

基本概念:定长内存池在申请内存时是直接向堆申请的,没有使用malloc,效率得到提升,而目前我们在本项目中用到到了很多new的操作,其本质还是malloc,因此我们要用定长内存池中的New()和Delete()函数来代替new和delete,进行内存申请和释放,从而提高程序执行效率 

准备工作:在某个涉及new或者delete的类中新增ObjectPool< ?>类型的私有成员变量,下面以PageCache为例

class PageCache
{
public:...
private:...ObjectPool<Span> _spanPool;//<>中的类型根据需要进行更改...
};

替换方式:将PageCache.cpp中所有使用new的地方都换成_spanPool.New(),将所有使用delet的位置都换为_spanPool.Delete( ? )(?表示要删除的对象的名称可能是span也可能是kspan之类的)

//Span* kSpan = new Span;
Span* kSpan = _spanPool.New();//delete span;
_spanPool.Delete(span);

易忽略位置

1、ConcurrentAlloc.h中的new ThreadCache

//pTLSThreadCache = new ThreadCache;
static ObjectPool<ThreadCache> tcPool;//static修饰保证只在当前文件中可以被访问
pTLSThreadCache = tcPool.New();

!!!重点!!! 

注意事项:pTLSThreadCache是每个线程独有的一个对象,但是为其申请空间的tcPool不是,它是一个静态的对象,整个进程中独一份,被当前进程中的所有线程共享,多线程情况下会出现线程安全问题,所以这里也需要加锁(不加的话有小概率不崩溃,即轮到t2执行时_memory不为空)

解决办法:在ObjPool类中新增公有成员变量_poolMtx,同时在pTLSThreadCache = tcPool.New()的两侧加锁

tcPool._poolMtx.lock();
pTLSThreadCache = tcPool.New();
tcPool._poolMtx.unlock();

补充:SpanList类中为了创建头结点的new Span不用替换,因为头节点通常在 SpanList 对象的整个生命周期内存在,并且不会像其他 Span 对象那样频繁创建和销毁。使用 _spanPool 进行内存管理主要是为了优化频繁分配和回收的对象,而头节点的长生命周期使得使用 _spanPool 的优势不明显

释放对象时不传对象大小

基本概念:在之前释放内存时我们不仅要传入释放的内存的地址,还要存放要释放的内存的大小,过于麻烦,所以最好只传递一个指针即可释放内存

ConcurrentFree(p1, 6);

补充内容1

在span类中新增一个表示当前span中管理的内存的大小的成员变量_objSize

struct Span
{...size_t _objSize = 0;//当前span管理的内存大小
};

补充内容2

在ConcurrentFree中,将MapObjectToSpan的位置进行移动,并获取当前span的_objSize

//释放内存
static void ConcurrentFree(void* ptr)
{Span* span = PageCache::GetInstance()->MapObjectToSpan(ptr);size_t size = span->_objSize;if (size > MAX_BYTES){...}else{...}
}

 补充内容3

在ConcurrentAlloc中从堆上获取到一个span后,补充该span的_objSize

//申请内存
static void* ConcurrentAlloc(size_t size)
{if (size > MAX_BYTES){...Span* span = PageCache::GetInstance()->NewSpan(kpage);span->_objSize = size;...}else{...}
}

补充内容4

CentralCache中的NewSpan后,填充该span的_objSize

//为指定位置桶下的SpanList申请一个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{...Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));//从PageCache中获取一个新的非空spanspan->_isUse = true;span->_objSize = size;....
}

 注意事项:记得最后把ConcurrentFree的第二个形参删除

测试函数

void WithNoSize()
{void* p1 = ConcurrentAlloc(257 * 1024);ConcurrentFree(p1);void* p2 = ConcurrentAlloc(129 * 8 * 1024);ConcurrentFree(p2);
}

为哈希桶加锁

基本概念:C++的标准模板库(STL)提供的容器在多线程环境下并不保证线程安全,因此在多个线程同时访问或修改同一个容器时,通常需要自行添加同步机制(如互斥锁)以确保数据的一致性和避免竞态条件,因此当我们尝试在本项目中使用哈希桶记录span与页号的映射关系时,需要及时的加锁

加锁位置:参与读写哈希桶的函数有NewSpan、MapObjectToSpan和ReleaseSpanToPageCache,它们都在PageCache.cpp中,其中在CentralCache.cpp中使用这三个函数时,只有MapObjectToSpan没有添加锁,这就可能导致多个线程在CentralCache中同时访问MapObjectToSpan函数并同时访问哈希桶造成线程安全问题,所以要在MapObjectToSpan函数执行到访问哈希桶的操作前加锁

//地址->页号->span的映射
Span* PageCache::MapObjectToSpan(void* obj)
{size_t id = ((size_t)obj >> PAGE_SHIFT);std::unique_lock<std::mutex> lock(_pageMtx);auto ret = _idSpanMap.find(id);if (ret != _idSpanMap.end()){return ret->second;}else{assert(false);return nullptr;}
}

关于std::unique_lock<std::mutex> lock(_pageMtx):是一种通过RAII方式管理互斥锁的机制,确保在多线程环境中对共享资源的安全访问。它自动处理锁的获取和释放,减少了手动管理锁可能带来的错误风险,同时提供了较高的灵活性,适用于各种复杂的同步场景

多线程环境下对比malloc测试

新增Benchmark.cpp源文件

#include<cstdio>
#include<iostream>
#include<vector>
#include<thread>
#include<mutex>
#include"ConcurrentAlloc.h"
using namespace std;void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)//ntime一轮申请和释放内存的次数,round是跑多少轮,nworks是线程数
{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次: 花费:%u ms\n",nworks, rounds, ntimes, malloc_costtime.load());printf("%u个线程并发执行%u轮次,每轮次free %u次: 花费:%u ms\n",nworks, rounds, ntimes, free_costtime.load());printf("%u个线程并发malloc&free %u次,总计花费:%u ms\n",nworks, nworks * rounds * ntimes, malloc_costtime.load() + free_costtime.load());
}// 单轮次申请释放次数 线程数 轮次
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){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轮次,每轮次concurrent alloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, malloc_costtime.load());printf("%u个线程并发执行%u轮次,每轮次concurrent dealloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, free_costtime.load());printf("%u个线程并发concurrent alloc&dealloc %u次,总计花费:%u ms\n",nworks, nworks * rounds * ntimes, malloc_costtime.load() + free_costtime.load());
}
int main()
{size_t n = 10000;cout << "==========================================================" << endl;BenchmarkConcurrentMalloc(n, 10, 10);cout << endl << endl;BenchmarkMalloc(n, 10, 10);cout << "==========================================================" << endl;return 0;
}

性能瓶颈分析

针对性能瓶颈使用基数树进行优化

基数树代码

 ~over~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/435230.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面

【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面 废话几句废话不多说&#xff0c;直接上源码目录结构init.pysimulator.pysimple_simulator_app.pyvehicle_config.json 废话几句 自动驾驶开发离不开仿真软件成品仿真软件种类多https://zhuanlan.zhihu.com/p/3…

推荐4款2024年热门的PDF转ppt工具

有时候&#xff0c;我们为了方便&#xff0c;需要将PDF里面的内容直接转换的PPT的格式&#xff0c;既方便自己演示和讲解&#xff0c;也让我们可以更加灵活的进行文件的编辑和修改。如果大家不知道要如何进行操作的话&#xff0c;我可以为大家推荐几个比窘方便实用的PDF转换工具…

UnityComputeShader Challenge1

UnityComputeShader Challenge1 using UnityEngine; using System.Collections;public class Challenge1 : MonoBehaviour {//editor拖拽对应的 compute文件public ComputeShader shader;//纹理的大小public int texResolution 1024;//着色器Renderer rend;//用于存储计算输出…

“国家高新”与“专精特新”企业的对比分析

近年来&#xff0c;企业经常听到“专精特新”和“高新技术”这两个热词。部分企业对于“专精特新”与“高新技术”之间的联系与区分却不够明确&#xff0c;其实二者之间既有重叠&#xff0c;又有模糊。“专精特新”和“高新技术”都是高技术、高核心竞争力的标志&#xff0c;都…

Excel提取数据

Excel提取数据 在使用Excel的过程中&#xff0c;我需要将带有评语的评分的文本内容提取出评分&#xff0c;如下所示 其中分数与文本之间用空格分隔&#xff0c;只有分数的评语会自动靠右对齐&#xff0c;我需要做的就是将B列的评语从中提取出分数放到C列中&#xff0c;以下为实…

【Linux第五课-进程上】PCB内部属性、标识符、进程状态、Linux下的进程状态、进程的优先级、Linux进程的调度与切换

目录 体系结构 -- 硬件上操作系统 -- 软件上进程PCB内部属性1、在linux里面看程序2、标识符获取程序的标识符父进程标识符 PPID查看进程的另一种方法通过系统调用创建进程 - fork杀掉一个进程for循环创建多个代码 3、进程状态进程排队 - 队列教程上关于进程状态表述运行阻塞&am…

【Android 14源码分析】Activity启动流程-2

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

PingCastle:一款针对活动目录AD的安全强化工具

关于PingCastle PingCastle是一款针对活动目录AD的安全强化工具&#xff0c;可以帮助广大研究人员提升活动目录的安全性&#xff0c;该工具甚至可以做到在 20% 的时间内实现 80% 的AD安全性。 Ping Castle 是一种旨在使用基于风险评估和成熟度框架的方法快速评估活动目录AD 安…

pywinauto,一款Win自动化利器!

1.安装 pywinauto是一个用于自动化Python模块&#xff0c;适合Windows系统的软件&#xff08;GUI&#xff09;&#xff0c;可以通过Pywinauto遍历窗口&#xff08;对话框&#xff09;和窗口里的控件&#xff0c;也可以控制鼠标和键盘输入&#xff0c;所以它能做的事情比之前介…

大数据实时数仓Hologres(一):Hologres 简单介绍

文章目录 Hologres 简单介绍 一、什么是实时数仓 Hologres 二、产品优势 1、专注实时场景 2、亚秒级交互式分析 3、统一数据服务出口 4、开放生态 5、MaxCompute查询加速 6、计算存储分离架构 三、应用场景 搭建实时数仓 四、产品架构 1、Shared Disk/Storage &am…

Appium独立测试自动化初始化脚本

1、查看环境初始化参数 确保appium已经开起来了&#xff0c;设置ip ,并点击启动 打开夜神模拟器&#xff0c;点击工具--设置 最下面的版本说明&#xff0c;双击进去 版本号这里再去单击。 直到进入到开发者模式。 可能我们不是开发者模式打开的状态&#xff0c;所以软件访问模…

我摊牌了!!程序员不卷了,AI大模型先转一步!

计算机行业小伙伴儿们的归宿到底在哪里啊&#xff1f; 互联网和程序员的风口已经过去了&#xff0c;作为一名大龄程序员&#xff0c;在继续卷和转型发展之间我选择了转行AI大模型。 4年更火的无非就是人工智能了&#xff0c;大家在招聘网站也可以看到名种各样的offer&#x…

基于Java+VUE+echarts大数据智能道路交通信息统计分析管理系统的设计与实现

大数据智能交通管理系统是一种基于Web的系统架构&#xff0c;通过浏览器/服务器&#xff08;B/S&#xff09;模式实现对城市交通数据的高效管理和智能化处理。该系统旨在通过集成各类交通数据&#xff0c;包括但不限于车辆信息、行驶记录、违章情况等&#xff0c;来提升城市管理…

天龙八部怀旧单机微改人面桃花+安装教程+GM工具+虚拟机一键端

今天给大家带来一款单机游戏的架设&#xff1a;天龙八部怀旧单机微改人面桃花。 另外&#xff1a;本人承接各种游戏架设&#xff08;单机联网&#xff09; 本人为了学习和研究软件内含的设计思想和原理&#xff0c;带了架设教程仅供娱乐。 教程是本人亲自搭建成功的&#xf…

画个心,写个花!Python Turtle库带你玩转创意绘图!

文章目录 前言一、Turtle 库基础介绍二、画布设置三、画笔属性设置1.画笔颜色设置2.画笔粗细与速度设置3.画笔形状设置 四、画笔移动函数五、画笔控制函数六、实战案例一&#xff1a;“花”字绘制七、实战案例二&#xff1a;心型图案绘制总结 前言 Python 的 turtle 库是一种简…

关于宝塔PHP getenv无法获取环境变量问题解决办法

今天有用ThinkPHP8接入阿里云OSS时&#xff0c;需要用的用到getenv()来读取环境变量&#xff0c;因为新版OSS SDK是用环境变更来设置AK的。 现象 正常执行PHP文件&#xff0c;可以取到环境变量&#xff1b;但是通过nginxphp-fpm调用脚本取到不到环境变量 原因 php-fpm为了防止…

[spring]用MyBatis XML操作数据库 其他查询操作 数据库连接池 mysql企业开发规范 动态sql

文章目录 一. MyBatis XML配置文件1. 配置链接字符串和MyBatis2. 写持久层代码方法定义Interface方法实现xml测试 3. 增删改查增:删改查 二. 开发规范(mysql)三. 其他查询操作1. 多表查询2. #{} 和 ${}(面试题)使用区别 排序功能like查询 三. 数据库连接池四. 动态sql1. < i…

使用shc|gzexe加密shell脚本,并配置全局可用

前言&#xff1a;堡垒机上多厂家共用&#xff0c;导致脚本需要加密并设置过期时间 一、使用shc&#xff08;二进制&#xff09; 1、安装shc sudo yum install epel-release sudo yum -y install shc sudo yum -y instal epel-release && sudo yum -y install shc 2、…

图像增强论文精读笔记-Deep Retinex Decomposition for Low-Light Enhancement(Retinex-Net)

1. 论文基本信息 论文标题&#xff1a;Deep Retinex Decomposition for Low-Light Enhancement 作者&#xff1a;Chen Wei等 发表时间和期刊&#xff1a;2018&#xff1b;BMVC 论文链接&#xff1a;https://arxiv.org/abs/1808.04560 2. 研究背景和动机 低光照条件下拍摄的…

计算机毕业设计 基于 Hadoop平台的岗位推荐系统 SpringBoot+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…