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

C++项目——高并发内存池(3)--central cache整体设计

1.central cache的介绍

1.1框架思想

1.1.1哈希映射

centralcache其实也是哈希桶结构的,并且central cachethread cacha哈希映射关系是一致的。目的为了,当thread cache某一个哈希桶下没有内存块时,可以利用之前编写的SizeClass::Index() 直接访问centralcache对应的哈希桶结构以拿到内存空间。

不同的是thread cache桶结构下面挂的是一个一个切好的定长内存块,而central cache桶结构下面挂的是一个一个的SpanList结构,其中的Span是管理以页为单位的大块内存,Span中大块内存被按照映射关系切成一个个小内存块,挂在Span自由链表中。

1.1.2 单例模式

因为,在高并发线程池的整体项目框架下,所有的thread cache都共享一个central cache,所以将它设计成单例模式是和逻辑且安全的。

1.1.3 加桶锁

但是central cache是线程之间共有的,所以线程从这里申请内存时,是需要加锁的。为了减少锁的竞争,central cache使用的是桶锁,意思是,当线程1和线程2同时向同一个桶申请内存时,才会有竞争,可以减少阻塞。

1.2 cenctral cache的作用

  • 分配更多的内存给thread cache

因为仅在thread cache给予内存是无锁的,效率更高。所以当它对应哈希映射的哈希桶内没有内存块时,就需要central cache来提供,那如果一次仅仅提供一个对应的内存块,冲突和阻塞现象会加重,即需要一次分配给一串。

  • 中央调度工作

由于thread cache一直向centralcache申请某一字节的内存空间,而当这些内存释放时,就会挂在thread cache下的哈希桶结构中,而导致其他线程再向centralcache申请时,没有对应的内存空间,所以centralcache还需要起到调度的作用,当thread cache桶结构挂的内存块过多时,需要拿回来。

 2.Span SpanList

 Span

struct Span
{PAGE_ID _pageId=0;// 大块内存起始页的页号size_t _n=0;//数量Span* _next=nullptr;Span* _prev=nullptr;size_t _useCount=0;//被使用的个数void* _freeList=nullptr;//切好的小块内存的自由链表
};

给默认值的意义是可以偷懒不写构造函数。


SpanList

之前提到central cache需要桶锁,总不能208个一个一个都显示的写吧。而且锁作为公有成员变量没有什么安全问题。所以直接在哈希桶下挂的SpanList中封装一把锁,这样每个桶就都有一把锁。

//每个桶下面挂着的就是SpanList
class SpanList
{ 
public:std::mutex _mtx;//桶锁SpanList(){_head = new Span;_head->_prev = _head;_head->_next = _head;}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;}void Erase(Span* pos){assert(pos);assert(pos != _head);Span* prev = pos->_prev;Span* next = pos->_next;prev->_next = next;next->_prev = prev;//注意不需要free 因为我用完要放到自由链表里面 供以后使用//从当前剔除就行}
private:Span* _head=nullptr;};

3.Central Cache的实现

 声明

class CentralCache
{
public:static CentralCache* GetInstance(){return &_sInst;}//从中心缓冲中获取多少数量的对象给thread cachesize_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size);// 获取一个非空的spanSpan* GetOneSpan(SpanList& list, size_t byte_size);
private:CentralCache(){}CentralCache(const CentralCache& abc) = delete;
private:static CentralCache _sInst;//记得类外初始化SpanList _spanLists[NFREELISTS]; 
};

 部分实现

#include "CentralCache.h"
CentralCache CentralCache::_sInst;Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{// 需要在自身判断 也需要在page cache中判断 所以以后在写// 这里只是为了通过编译return nullptr;
}

3.1 ThreadCache::FetchFromCentralCache()

记得在thread cache中我们没有实现的这个函数吗,现在我们既然有了CentralCache类型了,现在来构思一下这个函数。

void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)

首先这个函数是需要返回一个地址的,就是我们将分割好的内存的起始位置当做返回值返回。其次我们通过上面得知CentralCache一次可不仅仅分给我们一个内存块,而是多个。

因此

  • 我们需要知道当空间充足时,分配几个。空间不充足时,分配几个。
  • 第二我们也需要知道这群内存块的最后一个的起始位置,方便我们把这一串挂到threadcache下的哈希桶结构内。
  • 如何将一串连续的空间都挂到桶上呢?难道每次都一个个的放吗?

3.1.1 SizeClass::NumMoveSize()

该用于确定分配数量的上限,即不可能分配出比该函数返回值更大的数量了。

class SizeClass
{
public://... 之前写过的略static size_t NumMoveSize(size_t size){assert(size > 0);int num = MAX_BYTES / size;if (num < 2) num = 2;//如果内存块比较大 最少一次拿走两个if (num > 512) num = 512;//如果内存块很小 最多一次拿走512个return num;}
};

假设size=8,难道就真的按照返回值512,一次性给一个thread cache分配出512个内存块吗?首先如果是这样,那么只会允许两次向central cache申请,其次给的空间过多,都在某一线程的自由链表挂着,其他线程想要用的时候拿不到,会使central cache的调度次数大大增加。

 

所以我们还需要一个缓慢增加申请数量的代码。

如果一个桶经常向central cache申请空间,说明该映射下的内存块需求量大,我们就依次进行一个缓慢增加的策略,随着他申请次数的增多,一次分配的内存块数量也增多。那要如何保存他申请的次数呢?

直接封装在thread cache桶(FreeList)里面

class FreeList
{
public:void Push(void* obj){}void* Pop(){}bool Empty(){}size_t& Maxsize(){return _maxSize;}
private:void* _freeList=nullptr;size_t _maxSize = 1;
};

3.1.2  FreeList::PushRange()

static void*& NextObj(void* obj)
{return *(void**)obj;
}
class FreeList
{
public:void PushRange(void* start, void* end){NextObj(end) = _freeList;_freeList = start;}size_t& Maxsize(){return _maxSize;}
private:void* _freeList=nullptr;size_t _maxSize = 1;
};

3.1.3 函数实现

这里假设我们已经实现好了一个函数FetchRangeObj(),它可以直接返回申请空间的真实数量。

void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{size_t batchNum = std::min(SizeClass::NumMoveSize(size),_freeLists[index].Maxsize());if (_freeLists[index].Maxsize() == batchNum) _freeLists[index].Maxsize() += 1;void* start = nullptr;void* end = nullptr;//batchNum只是理想的 申请的对象 但事实是可能只能申请一个 而申请不到期望值size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);if(actualNum == 1){assert(start == end);return start;}else{_freeLists[index].PushRange(NextObj(start), end);return start;}return nullptr;
} 

 3.2 CentralCache::FetchRangeObj()

经过上面的分析,ThreadCache::FetchFromCentralCache()中需要用到CentralCache::FetchRangeObj(),以拿到start end指针,以及确切的可以分配给thread cache的内存块数量。

首先,需要通过size找到哈希桶的下标,然后找一个非空的Span,用来申请空间。

其次,如果span->_freeList不为空就说明至少有一个内存块,所以actualNum初始值为1,所以end也只需向后走batchNum-1次,将[start,end]这部分拿走,使_freeList指向end后面的那个节点,即使那个节点是空也符合逻辑。

然后,依据batchNum获取内存块数,可能会不够,但至少有一个可以被拿走使用解决问题,所以处理的逻辑是,如果不能获取期望的内存块数,那就有几个拿几个。当end的后面为空时停止。

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{//首先依据申请的对齐字节数判断在哪一个哈希桶内size_t index = SizeClass::Index(size);//上锁_spanLists[index]._mtx.lock();//要给别人分配内存 首先从自己这里找一个非空的Span 自己没有就从page cache找一个Span* span = GetOneSpan(_spanLists[index],size);assert(span);assert(span->_freeList);start = span->_freeList;end = start;int i = 0;size_t actualNum = 1;while (i < batchNum - 1 && NextObj(end) != nullptr){end = NextObj(end);i++;}span->_freeList = NextObj(end);NextObj(end) = nullptr;_spanLists[index]._mtx.unlock();return actualNum;
}

4.thread cache向central cache申请空间的流程图

流程图

相关文章:

C++项目——高并发内存池(3)--central cache整体设计

1.central cache的介绍 1.1框架思想 1.1.1哈希映射 centralcache其实也是哈希桶结构的&#xff0c;并且central cache和thread cacha的哈希映射关系是一致的。目的为了&#xff0c;当thread cache某一个哈希桶下没有内存块时&#xff0c;可以利用之前编写的SizeClass::Index…...

Spring Boot 整合 MyBatis 配置等案例教程

运行环境&#xff1a;JDK 7 或 8、Maven 3.0 技术栈&#xff1a;SpringBoot 1.5、SpringBoot Mybatis Starter 1.2 、MyBatis 3.4 前言 距离第一篇 Spring Boot 系列的博文 3 个月了。《Springboot 整合 Mybatis 的完整 Web 案例》第一篇出来是 XML 配置 SQL 的形式。虽然 XM…...

比特数据结构与算法(第三章_下)队列的概念和实现(力扣:225+232+622)

一、队列&#xff08;Queue&#xff09;队列的概念&#xff1a;① 队列只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。② 入队列&#xff0c;进行插入操作的一端称为 队尾。出队列&#xff0c;进行删除操作的一端称为 队头。③ 队列中的元素…...

c++提高篇——STL容器实现打分系统

一、案例说明 有5名选手:选手ABCDE&#xff0c;10个评委分别对每一名选手打分&#xff0c;去除最高分&#xff0c;去除评委中最低分&#xff0c;取平均分。 二、案例实现 在实现这个系统时&#xff0c;我们规划一下实现的步骤以及细节&#xff1a; 1、创建一个选手类&#x…...

【图片上传记录三】element-ui组件详解与封装(自定义上传、限制文件大小、格式以及图片尺寸)

业务上有需求是前端上传 jpg/png/gif 格式, 并且 尺寸为 150px * 150px,300px*300px,428*428px 的图片 同时在上传的同时需要携带用户的个人信息以及其他额外信息 因此在 element-upload 基础之上 实现这个需求需要在上传前检查图片的大小&#xff0c;格式以及尺寸如何上传也成…...

一个golang版本管理工具

GitHub - moqsien/gvc: GVC is a productive tool to manage your dev environment for multi platforms and machines. | GVC 是一个用于快速配置和管理多机器跨平台的开发环境的生产力工具。 目前&#xff0c;gvc拥有以下功能或特点&#xff1a; go编译器自动安装和添加环…...

SpringBoot整合Spring Security过滤器链加载执行流程源码分析

文章目录1.引言2.Spring Security过滤器链加载1.2.注册名为 springSecurityFilterChain的过滤器2、查看 DelegatingFilterProxy类3.查看 FilterChainProxy类3.1 查看 doFilterInternal方法。3.2 查看 getFilters方法。4 查看 SecurityFilterChain接口5 查看 SpringBootWebSecur…...

Jest使用

一、测试到底测什么 提到测试的时候&#xff0c;即使是最简单的一个代码块可能都让初学者不知所措。最常问的问题的是“我怎么知道要测试什么&#xff1f;”。如果你正在写一个 Web 应用&#xff0c;那么你每个页面每个页面的测试用户交互的方式&#xff0c;就是一个很好的开端…...

定位于企业数字化底座,开箱可用(spring cloud+Vue)基础框架,赶紧收藏!

项目介绍&#xff1a;JVS是什么&#xff1f;JVS是企业级应用构建的基础脚手架&#xff0c;提供开箱即用的基础功能集成&#xff0c;其中集成了账户管理、租户管理、用户权限体系、三方登录、环境配置、各种业务日志等功能&#xff0c;还提供了对接低代码、数据中台的能力。JVS能…...

java字符统计

问题描述 给定一个只包含大写字母的字符串 &#xfffd; S, 请你输出其中出现次数最多的字符。 如果有多个字母均出现了最多次, 按字母表顺序依次输出所有这些字母。 输入格式 一个只包含大写字母的字符串 &#xfffd; S. 输出格式 若干个大写字母&#xff0c;代表答案。 …...

C#:Krypton控件使用方法详解(第八讲) ——kryptonBreadCrumb

今天介绍的Krypton控件中的kryptonBreadCrumb&#xff0c;下面开始介绍这个控件的属性&#xff1a;首先要介绍的是RootItem属性和外观属性&#xff1a;RootItem属性组中包含属性如下&#xff1a;image属性&#xff1a;代表在文字对象的前方插入一个图片&#xff0c;属性值如下图…...

2023从0开始学性能(1) —— 性能测试基础【持续更新】

背景 不知道各位大佬有没遇到上面的情况&#xff0c;性能这个东西到底是什么&#xff0c;还是以前的358原则吗&#xff1f;明显并不是适用于现在了。多次想踏入性能测试门槛都以失败告终&#xff0c;这次就以系列的方式来督促自己真正踏进性能测试的门槛。 什么是性能测试 通…...

如何通过一台 iPhone 申请一个 icloud 邮箱账号 后缀为 @icloud.com

总目录 iOS开发笔记目录 从一无所知到入门 文章目录需求关键步骤步骤后续需求 在 iPhone 自带的邮箱软件中添加账号&#xff0c;排第一位的是 iCloud 邮箱&#xff1a; 选 iCloud 之后&#xff1a; 提示信息是exampleicloud.com&#xff0c;也就是说是有icloud.com为域的邮箱…...

SQL89 计算总和

描述OrderItems表代表订单信息&#xff0c;包括字段&#xff1a;订单号order_num和item_price商品售出价格、quantity商品数量。order_numitem_pricequantitya110105a211100a21200a421121a5510a2119a775【问题】编写 SQL 语句&#xff0c;根据订单号聚合&#xff0c;返回订单总…...

Netty高级应用之:编解码器与群聊天室开发

Netty高级应用之&#xff1a;编解码器与群聊天室开发 文章目录Netty高级应用之&#xff1a;编解码器与群聊天室开发Netty编解码器Java的编解码Netty编解码器概念解码器(Decoder)编码器(Encoder)编码解码器CodecNetty案例-群聊天室聊天室服务端编写聊天室客户端编写Netty编解码器…...

Vue的生命周期

Vue的生命周期是指Vue实例从创建到销毁的过程&#xff0c;它包括了以下几个阶段&#xff1a;初始化、编译、挂载、更新、渲染和销毁。 初始化&#xff1a;Vue实例创建时&#xff0c;会执行初始化过程&#xff0c;主要包括以下几个步骤&#xff1a; 初始化数据&#xff1a;Vue…...

MySQL —— 数据库基础

文章目录1. centos7 安装Mysql2. 数据库的概念3. 数据库下创建库&#xff0c;表4. 库&#xff0c;表 的本质5. 数据库服务器 和 库 &#xff0c;表的关系6. MySQL架构7. 存储引擎前言&#xff1a; 数据库是对数据进行管理的软件。1. centos7 安装Mysql 需要把系统自带的MySQL给…...

多线程知识点

多线程 基本知识 创建线程的常用三种方式&#xff1a; 继承Thread类实现Runnable接口实现Callable接口&#xff08;JDK1.5>&#xff09; public class ThreadTest extends Thread {Overridepublic void run() {System.out.println(this.getName() "..开始.."…...

有序表之红黑树

文章目录1、五个条件2、调整策略2.1 插入调整的情况2.1.1 情况一&#xff1a;插入节点是红色&#xff0c;其父节点也是红色2.1.2 情况二2.1.2 代码实现2.2 删除调整的情况2.2.1 情况一&#xff1a;双重黑节点的兄弟节点也是黑色&#xff0c;且其兄弟的两个孩子也是黑色2.2.2 情…...

HTTP状态码都有哪些?

100&#xff1a;客户必须继续发出请求 101&#xff1a;客户要求服务器根据请求转换HTTP协议版本 二&#xff1a;2xx 200&#xff1a;交易成功 201&#xff1a;提示知道新文件的URL 202&#xff1a;接受和处理、但处理未完成 203&#xff1a;返回信息不确定或不完整 204&#…...

高性能Windows流媒体服务器部署:5大核心技术与3种实战架构深度解析

高性能Windows流媒体服务器部署&#xff1a;5大核心技术与3种实战架构深度解析 【免费下载链接】srs-windows 项目地址: https://gitcode.com/gh_mirrors/sr/srs-windows 在Windows平台上构建专业级流媒体服务系统&#xff0c;需要综合考虑协议兼容性、性能优化和部署架…...

[智能体-69]:重新认知MCP:协议不生产智能,只是AI全域交互的标准化基石

MCP只是提供了大模型、编排调度、外部工具能够进行结构化交流的标准&#xff0c;而整个系统的智能主要依赖编排调度&#xff0c;与外部软件系统的交互取决于外部工具&#xff0c;包括外部语音交互、视觉交互、数字化交互。当下MCP&#xff08;Model Context Protocol&#xff0…...

别再用SonarQube凑数了!DeepSeek原生圈复杂度引擎的6大颠覆性能力(含GitHub私有部署密钥)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek圈复杂度分析的底层原理与范式革命 DeepSeek圈复杂度分析并非传统McCabe度量的简单复刻&#xff0c;而是基于控制流图&#xff08;CFG&#xff09;动态重构与语义感知路径裁剪的双重机制构建的新范式。…...

QMCDecode终极指南:3步解锁QQ音乐加密格式,实现跨平台音乐自由

QMCDecode终极指南&#xff1a;3步解锁QQ音乐加密格式&#xff0c;实现跨平台音乐自由 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac&#xff0c;qmc0,qmc3转mp3, mflac,mflac0等转flac)&#xff0c;仅支持macOS&#xff0c;可自动识别到QQ音乐下载目…...

Transient、QuickEye、VerifyEye傻傻分不清?一文讲透Ansys里三种眼图仿真方法的适用场景与避坑指南

Transient、QuickEye、VerifyEye深度解析&#xff1a;Ansys眼图仿真技术选型实战指南 在高速数字系统设计中&#xff0c;眼图分析是评估信号完整性的黄金标准。面对Ansys工具链中三种截然不同的眼图生成方法&#xff0c;工程师常常陷入选择困境——是追求精确度的传统瞬态分析&…...

基于MaixCam的延时摄影系统:从硬件选型到Python编程全解析

1. 项目概述&#xff1a;用MaixCam打造你的专属延时摄影工坊延时摄影&#xff0c;这个听起来有点专业、甚至带点“魔法”色彩的词&#xff0c;其实离我们并不遥远。想想看&#xff0c;把一朵花从含苞到绽放的几天时间&#xff0c;压缩成十几秒的惊艳绽放&#xff1b;或者把一座…...

榨干Codex!OpenAI工程师亲授Codex真正用法

你可能把 Codex 当编程助手用&#xff0c;改改代码&#xff0c;跑跑测试。但它的能力远不止于此。OpenAI 的客户支持工程师 Jason&#xff08;jxnlco&#xff09;告诉你&#xff0c;Codex 其实是一套完整的电脑工作系统&#xff0c;从语音输入到自动化&#xff0c;从浏览器操控…...

基于Cynthion逆向USB协议,为DP100电源开发Linux控制软件

1. 项目概述&#xff1a;用Cynthion嗅探USB&#xff0c;为DP100电源打造Linux软件作为一名长期在Linux环境下折腾硬件和嵌入式开发的爱好者&#xff0c;我经常遇到一个头疼的问题&#xff1a;很多不错的桌面小设备&#xff0c;比如电源、示波器、逻辑分析仪&#xff0c;它们的官…...

PS5 NOR Modifier深度解析:如何通过Windows工具修复PS5硬件故障与实现光驱版转数字版

PS5 NOR Modifier深度解析&#xff1a;如何通过Windows工具修复PS5硬件故障与实现光驱版转数字版 【免费下载链接】PS5NorModifier The PS5 Nor Modifier is an easy to use Windows based application to rewrite your PS5 NOR file. This can be useful if your NOR is corru…...

掌握Umi-OCR:5分钟上手开源免费离线文字识别工具

掌握Umi-OCR&#xff1a;5分钟上手开源免费离线文字识别工具 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片&#xff0c;PDF文档识别&#xff0c;排除水印/页眉页脚&#xff0c;扫描/生成二维码。内置多国语言库。…...