【高性能内存池】page cache 5
page cache
- 1 page cache的框架
- 2 central cache从page cache申请n页span的过程
- 3 page cache 的结构
- 3.1 page cache类框架
- 3.2 central cache向page cache申请span
- 3.3 获取k页的span
page cache
的结构和central cache
是一样的,都是哈希桶的结构,并且挂载的都是span
。但是不同的是,central cache
为thread cache
服务,分配的是一个个freeList
,所以需要和thread cache
的映射规则一样,page cache
为central cache
服务,分配的是一个个span
。所以page cache
挂载的span
不会进行切分。
1 page cache的框架
下面的图中,左边是central cache的结构,右边是page cache的结构。
我们假设page cache
的桶为128页
大小(这个可以根据实际情况更改),为了让桶号和页号对应起来,我们让0号桶
为空,所以page cache
的哈希桶个数要设置为129
.
//page cache中哈希桶的个数
static const size_t NPAGES = 129;
2 central cache从page cache申请n页span的过程
下面是page cache的结构
当central cache
向page cache
申请n页
的span
时,page cache
的做法如下:
3 page cache 的结构
1. page cache是否需要加锁?
需要。因为每个线程都拥有一份
thread cache
,当thread cache
中没有内存时,会向central cache
中要。如果多个thread cache
同时访问central cache
的一个桶时,就会出现线程安全问题,就需要加桶锁。但是如果多个thread cache
访问不同的central cache
是不会出现线程安全问题的,也不需要加锁。而如果此时多个central cache
同时访问page cache
就会出现线程安全问题。
2. 那page cache加的锁是桶锁吗?
不是
central cache
的分配方式是每个桶独立的,所以可以使用桶锁。而根据上面描述的page cache
的分配方式,访问第n号桶很可能是从n + 1
号桶中拿到span
,分配给central cache
后,再将剩余的span
挂载到1号桶中。这种分配方式会涉及到多个桶,如果使用桶锁,锁住其中一个桶,其他的桶就无法进行协同了。所以page cache
加的是一把大锁,能将整个page cache
锁住。
4.page cache需要设计单例模式吗?
需要。整个进程中能有一个
page cache
,所以page cache
也需要设计一个单例模式。
3.1 page cache类框架
//单例模式
class PageCache
{
public://提供一个全局访问点static PageCache* GetInstance(){return &_sInst;}
private://static const size_t NPAGES = 129;SpanList _spanLists[NPAGES]; std::mutex _pageMtx; //大锁
private:PageCache() //构造函数私有{}PageCache(const PageCache&) = delete; //防拷贝static PageCache _sInst;
};
3.2 central cache向page cache申请span
当thread cache
向central cache
申请span
的时候,会从central cache
中获取一个非空的span
,在上篇文章中的实现是这样的:
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{// 查看当前的spanlist中是否有还有未分配对象的spanSpan* it = list.Begin();while (it != list.End()){if (it->_freeList != nullptr){return it;}else{it = it->_next;}}// 先把central cache的桶锁解掉,这样如果其他线程释放内存对象回来,不会阻塞list._mtx.unlock();return span;
}
但是上篇文章没有考虑如果central cache
中没有span
了该怎么办。
当central cache中没有span了该怎么办?
需要向
page cache
申请
central cache如何向page cache申请span?
1.计算出
thread cache
一次向central cache
申请的freeList
的个数num
2. 将freeList
的个数num
和thread cache
申请的size
字节相乘,计算出要申请的总字节数npage
3. 将npage
除以页的大小,最终得到central cache
要向page cache
申请多少个页。不满1页按1页算。
static size_t NumMovePage(size_t size){//计算thread cache找central cache要的freeList的个数size_t num = NumMoveSize(size);//计算申请的总的字节数size_t npage = num*size;//计算向page cache申请的总的页数npage >>= PAGE_SHIFT;//不满1页按1页算if (npage == 0)npage = 1;return npage;}
PAGE_SHIFT
表示页的大小,这里为13,因为2<sup>13</sup> = 8KB
static const size_t PAGE_SHIFT = 13;
当central cache
从page cache
中申请到span
之后,还需要将span
进行切分,切分成符合大小的freeList
挂在span
下面。
central cache是如何进行切分的?
1.计算
span
的大块内存的起始地址star
t:span
的起始页号乘以一页的大小即可得到这个span
的起始地址
2.计算span
的大小(字节数)bytes
:span
的页数乘以一页的大小
3.计算span的结束位置的地址end
:start + bytes
4.将span切分为size大小,全部尾插到原来的span->freeList后面。具体过程看下图。
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{// 查看当前的spanlist中是否有还有未分配对象的spanSpan* it = list.Begin();while (it != list.End()){if (it->_freeList != nullptr){return it;}else{it = it->_next;}}// 先把central cache的桶锁解掉,这样如果其他线程释放内存对象回来,不会阻塞list._mtx.unlock();// 走到这里说没有空闲span了,只能找page cache要PageCache::GetInstance()->_pageMtx.lock();Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));span->_isUse = true;span->_objSize = size;PageCache::GetInstance()->_pageMtx.unlock();// 对获取span进行切分,不需要加锁,因为这会其他线程访问不到这个span// 计算span的大块内存的起始地址和大块内存的大小(字节数)char* start = (char*)(span->_pageId << PAGE_SHIFT);size_t bytes = span->_n << PAGE_SHIFT;char* end = start + bytes;// 把大块内存切成自由链表链接起来// 1、先切一块下来去做头,方便尾插span->_freeList = start; start += size; void* tail = span->_freeList; int i = 1;while (start < end){++i;NextObj(tail) = start; //相当于tail->next = starttail = NextObj(tail); // tail = start;start += size;}NextObj(tail) = nullptr;// 切好span以后,需要把span挂到桶里面去的时候,再加锁list._mtx.lock();list.PushFront(span);return span;
}
3.3 获取k页的span
Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
上面是GetOneSpan
函数中用到的从page cache
中拿k页大小的span
的调用过程。
当central cache
向page cache
申请一个k页的span
时,如果page cache
中有k页的span
,就直接删除然后给central cache
就行了,如果没有,就要去堆中申请一个128页大小的span
了。
回顾之前central cache向page cache申请内存的过程。
当central cache向page cache申请一个k页大小的span时,由于page cache是直接映射,所以只需要去第k号桶中找是否有span就行了。如果没有,就去k + 1,k + 2…中找,直到找到第128号桶都没有时,再去堆中申请。
当page cache
向堆中申请128页大小的内存时,得到的是128页内存的起始地址,我们需要将这份起始地址转换为对应的页号,也就是将起始地址除以1页的大小。
//获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{assert(k > 0 && k < NPAGES);//先检查第k个桶里面有没有spanif (!_spanLists[k].Empty()){return _spanLists[k].PopFront();}//检查一下后面的桶里面有没有span,如果有可以将其进行切分for (size_t i = k + 1; i < NPAGES; i++){if (!_spanLists[i].Empty()){Span* nSpan = _spanLists[i].PopFront();Span* kSpan = new Span;//在nSpan的头部切k页下来kSpan->_pageId = nSpan->_pageId;kSpan->_n = k;nSpan->_pageId += k;nSpan->_n -= k;//将剩下的挂到对应映射的位置_spanLists[nSpan->_n].PushFront(nSpan);return kSpan;}}//走到这里说明后面没有大页的span了,这时就向堆申请一个128页的spanSpan* bigSpan = new Span;void* ptr = SystemAlloc(NPAGES - 1);bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;bigSpan->_n = NPAGES - 1;_spanLists[bigSpan->_n].PushFront(bigSpan);//尽量避免代码重复,递归调用自己return NewSpan(k);
}
为什么要递归调用?
因为
page cache
向堆申请了128页大小的内存,需要将其切分成k页的span和128-k页的span。本来需要编写切分的代码,但是为了避免写重复的代码,就再递归调用该函数,后面会自行切分的。
当central cache向page cache申请内存时,central cache对应的哈希桶是处于加锁的状态的,那在访问page cache之前我们应不应该把central cache对应的桶锁解掉呢?
这里建议在访问
page cache
前,先把central cache
对应的桶锁解掉。虽然此时central cache
的这个桶当中是没有内存供其他thread cache
申请的,但thread cache
除了申请内存还会释放内存,如果在访问page cache
前将central cache
对应的桶锁解掉,那么此时当其他thread cache
想要归还内存到central cache
的这个桶时就不会被阻塞。
因此在调用
NewSpan
函数之前,我们需要先将central cache
对应的桶锁解掉,然后再将page cache
的大锁加上,当申请到k页的span
后,我们需要将page cache
的大锁解掉,但此时我们不需要立刻获取到central cache
中对应的桶锁。因为central cache
拿到k页的span
后还会对其进行切分操作,因此我们可以在span
切好后需要将其挂到central cache
对应的桶上时,再获取对应的桶锁。
这里为了让代码清晰一点,只写出了加锁和解锁的逻辑,我们只需要将这些逻辑添加到之前实现的
GetOneSpan
函数的对应位置即可。
spanList._mtx.unlock(); //解桶锁
PageCache::GetInstance()->_pageMtx.lock(); //加大锁//从page cache申请k页的spanPageCache::GetInstance()->_pageMtx.unlock(); //解大锁//进行span的切分...spanList._mtx.lock(); //加桶锁//将span挂到central cache对应的哈希桶
相关文章:
【高性能内存池】page cache 5
page cache 1 page cache的框架2 central cache从page cache申请n页span的过程3 page cache 的结构3.1 page cache类框架3.2 central cache向page cache申请span3.3 获取k页的span page cache的结构和central cache是一样的,都是哈希桶的结构,并且挂载的…...
Vue 3 魔法揭秘:CSS 解析与 scoped 背后的奇幻之旅
文章目录 一、背景二、源码分析transformMain 返回值transformStyle 方法compileStyleAsync 方法scopedPlugin 方法template 添加 __scopeId 三、总结 一、背景 Vue 3 文件编译流程详解与 Babel 的使用 上文分析了 vue3 的编译过程,但是在对其中样式的解析遗留了一…...
如何获取钉钉webhook
第一步打开钉钉并登录 第二步创建团队 并且 添加自定义 机器人 即可获取webhook...
网页WebRTC电话和软电话哪个好用?
关于WebRTC电话与软件电话哪个更好用,这实际上取决于多个因素,并没有一个绝对的答案。不过,我可以根据WebRTC技术的一些特点,以及与传统软件电话相比的优劣势,为你提供一个清晰的对比。 首先,让我们了解一下…...
MySQL Mail服务器集成:如何配置发送邮件?
MySQL Mail插件使用指南?怎么优化 MySQL发邮件性能? MySQL Mail服务器的集成,使得数据库可以直接触发邮件发送,极大地简化了应用架构。AokSend将详细介绍如何配置MySQL Mail服务器,以实现邮件发送功能。 MySQL Mail&…...
【Rockchip系列】官方函数:imcopy
imcopy 函数原型 IM_STATUS imcopy(const rga_buffer_t src,rga_buffer_t dst,int sync 1,int *release_fence_fd NULL);功能说明 imcopy函数用于执行单次快速图像拷贝操作,将图像从源缓冲区拷贝到目标缓冲区。 参数说明 参数描述src[必填] 源图像缓冲区&…...
Matlab实现鲸鱼优化算法优化回声状态网络模型 (WOA-ESN)(附源码)
目录 1.内容介绍 2部分代码 3.实验结果 4.内容获取 1内容介绍 鲸鱼优化算法(Whale Optimization Algorithm, WOA)是一种基于座头鲸捕食行为的群智能优化算法。该算法通过模仿座头鲸使用螺旋形路径和包围猎物的策略来探索和开发解空间,以找到…...
迈威通信闪耀工博会,以创新科技赋能工业自动化
昨日,在圆满落幕的第24届中国国际工业博览会上,迈威通信作为工业自动化与智慧化领域的先行者,以“创新打造新质通信,赋能工业数字化”为主题精彩亮相,向全球业界展示了我们在工业自动化领域的最新成果与创新技术。此次…...
C# DotNetty客户端
1. 引入DotNetty包 我用的开发工具是VS2022,不同工具引入可能会有差异 工具——>NuGet包管理器——>管理解决方案的NuGet程序包 搜索DotNetty 2.新建EchoClientHandler.cs类 用于接收服务器返回数据 public class EchoClientHandler : SimpleChannelIn…...
4G模组SIM卡电路很简单,但也要注意这些坑
上次水SIM卡相关的文章,还是上一次; 上一篇文章里吹牛说,跟SIM卡相关的问题还有很多,目的是为下一篇文章埋下伏笔;伏笔埋是埋下了,但如果债老是不还,心里的石头就总悬着,搞不好老板…...
常见电脑品牌BIOS设置与进入启动项快捷键
常见电脑品牌BIOS与引导项快捷键速查表 | 电脑品牌 | BIOS快捷键 | 引导项快捷键 | 备注 ||------------|------------|--------------|------------------------------ || 联想 | F2/F1 | F12 | 笔记本通常为F2,台式机通常为F1 || IBM/ThinkPad | F1 | 未知 | ||…...
C语言中的日志机制:打造全面强大的日志系统
前言 在软件开发中,良好的日志记录机制对于调试、监控程序状态和维护系统的稳定性至关重要。本文将介绍如何在C语言中构建一个全面强大的日志系统,并提供一些示例代码。 1. 日志的基本概念 日志级别:用于分类日志信息的重要性,…...
局域网广域网,IP地址和端口号,TCP/IP 4层协议,协议的封装和分用
前言 在古老的年代,如果我们要实现两台机器进行数据传输, A员工就得去B员工的办公电脑传数据(B休息,等A传完),这样就很浪费时间 所以能不能不去B的工位的同时,还能传数据。这时候网络通信就出来…...
LabVIEW项目编码器选择
在LabVIEW项目中,选择增量式(Incremental Encoder)和绝对式(Absolute Encoder)编码器取决于项目的具体需求。增量式编码器和绝对式编码器在工作原理、应用场景、精度和成本等方面存在显著差异。以下从多方面详细阐述两…...
Spring Boot实现房产租赁业务逻辑
1 绪论 1.1 研究背景 中国的科技的不断进步,计算机发展也慢慢的越来越成熟,人们对计算机也是越来越更加的依赖,科研、教育慢慢用于计算机进行管理。从第一台计算机的产生,到现在计算机已经发展到我们无法想象。给我们的生活改变很…...
汽车3d动画渲染选择哪个?选择最佳云渲染解决方案
面临汽车3D动画渲染挑战?选择正确的云渲染服务至关重要。探索最佳解决方案,优化渲染效率,快速呈现逼真动画。 汽车3d动画渲染选择哪个? 对于汽车3D动画渲染,选择哪个渲染器取决于你的项目需求、预算和期望的效果。Ble…...
火语言RPA流程组件介绍--网页/元素截图
🚩【组件功能】:对整个网页、可见区域或者某个元素进行截图 ,保存至指定文件夹,仅适用于内置浏览器 配置预览 配置说明 截图类型 整个网页/可见区域/元素截图 目标元素 支持T或# 通过自动捕获工具捕获(选择元素工具使用方法)…...
VSCode编程配置再次总结
VScode 中C++编程再次总结 0.简介 1.配置总结 1.1 launch jsion文件 launch.json文件主要用于运行和调试的配置,具有程序启动调试功能。launch.json文件会启用tasks.json的任务,并能实现调试功能。 左侧任务栏的第四个选项运行和调试,点击创建launch.json {"conf…...
银行管理系统
摘 要 伴随着信息技术与互联网技术的不断发展,人们进到了一个新的信息化时代,传统管理技术性没法高效率、容易地管理信息内容。为了实现时代的发展必须,提升管理高效率,各种各样管理管理体系应时而生,各个领域陆续进到…...
极狐GitLab 17.4 重点功能解读【四】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...
[每日一练]利用自连接实现数量查询
该题目来源于力扣: 1731. 每位经理的下属员工数量 - 力扣(LeetCode) 题目要求: 表:Employees----------------------- | Column Name | Type | ----------------------- | employee_id | int | | name …...
Linux云计算 |【第四阶段】RDBMS1-DAY3
主要内容: 子查询(单行单列、多行单列、单行多列、多行多列)、分页查询limit、联合查询union、插入语句、修改语句、删除语句 一、子查询 子查询就是指的在一个完整的查询语句之中,嵌套若干个不同功能的小查询,从而一…...
初始MYSQL数据库(8)—— JDBC编程
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏: MYSQL 目录 JDBC的概念 JDBC的使用 加载驱动包 建立连接 创建 statement 对象 定义并执行SQL语句 处理结果集 关闭资源 SQL注入 …...
Vue $router.push打开新窗口
Vue $router.push打开新窗口 最近有粉丝小伙伴问我:$router.push方法用于在当前窗口中跳转路由,但有时候我们需要在新的窗口或标签页中打开一个路由改怎么实现呢? 那么这里就介绍下实现逻辑和代码案例! 文章目录 Vue $router.pus…...
SQL进阶技巧:如何利用if语句简化where或join中的条件 | if条件语句的优雅使用方法
目录 0 问题场景 1 数据准备 2 问题分析 2.1 需求一 2.2需求二 3 小结 0 问题场景 有两张表,一张用户下单表user_purchase(用户ID粒度)包含用户ID、订单ID和下单消耗金额和一张用户维表user_info包含用户ID、用户年龄和用户是否实名认证。 user_purchase user_info 需…...
SpringCloud-Alibaba第二代微服务快速入门
1.简介 Spring Cloud Alibaba其实是阿里的微服务解决方案,是阿里巴巴结合自身微服务实践,开源的微服务全家桶,在Spring Cloud项目中孵化成为Spring Cloud的子项目。第一代的Spring Cloud标准中很多组件已经停更,如:Eureak,zuul等。所以Sprin…...
JSON字符串转换成对象
在Java中,将JSON字符串转换成对象是一个常见的操作,特别是在处理Web服务或API时。这通常通过使用第三方库来实现,因为Java标准库(Java SE)本身并不直接支持JSON的序列化和反序列化。最常用的库之一是Jackson和Gson。下…...
第三十五章 结合加密和签名
文章目录 第三十五章 结合加密和签名使用非对称密钥签名并加密使用非对称密钥加密并签名 第三十五章 结合加密和签名 可以在同一条消息中加密和签名。在大多数情况下,只需组合前面主题中给出的方法即可。本主题讨论了多种场景。 使用非对称密钥签名并加密 要签名…...
FastAPI 第八课 -- 路径操作依赖项
目录 一. 前言 二. 依赖项(Dependencies) 2.1. 依赖注入 2.2. 依赖项的使用 三. 路径操作依赖项的基本使用 3.1. 预处理(Before) 3.2. 后处理(After) 四. 多个依赖项的组合 五. 异步依赖项 一. 前…...
大厂面试真题-说一下Mybatis的缓存
首先看一下原理图 Mybatis提供了两种缓存机制:一级缓存(L1 Cache)和二级缓存(L2 Cache),旨在提高数据库查询的性能,减少数据库的访问次数。注意查询的顺序是先二级缓存,再一级缓存。…...
域名注册网站推荐/如何制作网页链接教程
分词的重要性对于一个搜索引擎来说是相当重要的,英文的分词相对简单,因为英文的每个单词都具有天然的分隔符,但当遇到中文时,就显得无能为力了。 中文是世界上最复杂的语言之一,不同的字在不同的词语中可能代表不同的意…...
济南地铁建设/googleseo服务公司
[sizex-large][colorred]Java集合框架之fastutil [/color][/size][url]http://rensanning.iteye.com/blog/1548162[/url] fastutil扩展了 Java集合框架,通过提供特定类型的map、set、list和queue,以及小内存占用、快速访问和插入;也提供大&am…...
网站要怎么上传/互联网运营培训课程
1.明确使用场景最近公司业务对接的有外国人,咱家虽然也是英语6级,可也不敢打包票能听懂人家老外的口语啊,万一再带点方言口音“what are you going to do to die”其实是““what are you going to do today?”澳洲英语方言很重&a…...
企业网站建设基本流程/重庆seo服务
1、禁止复制文本html页面增加<style>*{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;} </style>复制代码2、禁止img标签增加样式<img style"pointer-even…...
泉州网站建设方案开发/百度手机助手下载安卓
在拆机前一定要拔掉笔记本的电池和台式机的电源线,还有断开CPU风扇的接线。 以免风扇转速过快产生电流损坏电子器件。 如除尘后开不了,可以尝试给主板放静电。 拆前人体放电 金手指使用橡皮擦擦拭,擦除氧化层。 可以的话更换硅胶。 键盘…...
search everything wordpress/长尾关键词挖掘
将企业软件中进行数据交换的业务对象,例如客户、订单或产品等常规模型进行标准化,让它们包含所有属性与关联信息,这种做法看上去似乎是一种吸引人的目标,但在Stefan Tilkov看来,这种方式将产生标准数据模型(…...