【高性能内存池】page cache 5

news2024/11/20 18:30:04

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 cachethread cache服务,分配的是一个个freeList,所以需要和thread cache的映射规则一样,page cachecentral 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 cachepage cache申请n页span时,page cache的做法如下:

1. 如果第n号桶里面有n页大小的span,就直接给central cache; 2.如果第n号桶中没有span,就去n + 1号桶中去查找,直到找到有span的桶; 3.假设n + 2号桶中有span,此时,将n + 2号桶中n页大小的span切给central cache,并将剩下的2页大小的span挂载到2号桶中; 4.如果找到128号桶都没有span,此时就向堆空间申请一个128页大小的span,挂载到128号桶下面,并将n页大小的span切给central cache,将剩下的span挂载到128-n号桶中。

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 cachecentral cache申请span的时候,会从central cache中获取一个非空的span,在上篇文章中的实现是这样的:

Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	// 查看当前的spanlist中是否有还有未分配对象的span
	Span* 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的个数numthread 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 cachepage cache中申请到span之后,还需要将span进行切分,切分成符合大小的freeList挂在span下面。

central cache是如何进行切分的?

1.计算span的大块内存的起始地址start:span的起始页号乘以一页的大小即可得到这个 span的起始地址
2.计算span的大小(字节数)bytesspan的页数乘以一页的大小
3.计算span的结束位置的地址endstart + bytes
4.将span切分为size大小,全部尾插到原来的span->freeList后面。具体过程看下图。
在这里插入图片描述

Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	// 查看当前的spanlist中是否有还有未分配对象的span
	Span* 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 = start
		tail = 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 cachepage 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个桶里面有没有span
	if (!_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页的span
	Span* 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页的span

PageCache::GetInstance()->_pageMtx.unlock(); //解大锁

//进行span的切分...

spanList._mtx.lock(); //加桶锁

//将span挂到central cache对应的哈希桶

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA全球互联同城速达国际版同城跑腿快递代取帮买帮送一体化服务系统源码

全球互联&#xff0c;便捷生活新篇章&#xff01; &#x1f31f; 开篇&#xff1a;跨越国界的即时服务革命 在这个快节奏的时代&#xff0c;你是否也曾为忙碌的生活而烦恼&#xff1f;购物、取件、送物……这些日常琐事似乎总在不经意间占据了我们宝贵的时间。但现在&#xf…

003集—— CAD批量划线和text文字(CAD—C#二次开发入门)

本例通过for循环创建255条线&#xff0c;颜色不同&#xff0c;并在线的右端点处注记文字。 效果如下: 本文有个事务的封装函数&#xff0c;如下&#xff1a; private ObjectId AppendEntity(Entity entity) { ObjectId objectId; Database db HostApplication…

❤Node实现接口增删改查(文章为例)

❤Node实现接口增删改查&#xff08;文章为例&#xff09; 1、文章表的创建​ 接下来我们新建一个文章数据表article&#xff0c;实现对于文章部分的管理功能接口 根据文章我们创建一个对应的 SQL 数据表 javascript CREATE TABLE articles (id INT AUTO_INCREMENT PRIMAR…

亚马逊卖家如何利用自养号测评策略低成本提升销量?

在跨境电商的平台上&#xff0c;随着市场逐渐成熟与竞争的白热化&#xff0c;众多卖家正面临流量增长乏力与转化率提升困难的双重挑战。为了在这日益激烈的竞争环境中脱颖而出&#xff0c;卖家们纷纷加大投入&#xff0c;探索多样化的推广策略&#xff0c;但往往因策略不够精准…

探索Kombo:AI与API的完美结合

文章目录 探索Kombo&#xff1a;AI与API的完美结合背景介绍库的定义安装指南简单函数使用场景应用常见问题及解决方案总结 探索Kombo&#xff1a;AI与API的完美结合 背景介绍 在当今快速发展的人工智能领域&#xff0c;Kombo库以其独特的优势脱颖而出。Kombo是一个专注于AI的…

2024年7月大众点评全国爱车前百名城市分析

在做一些城市分析、学术研究分析、商业选址、商业布局分析等数据分析挖掘时&#xff0c;大众点评的数据参考价值非常大&#xff0c;截至2024年7月&#xff0c;大众点评美食店铺剔除了暂停营业、停止营业后的最新数据情况分析如下。 分析研究的字段维度包括大众点评数字id、字母…

微信商城小程序怎么弄_重塑购物体验

在数字化浪潮的推动下&#xff0c;微信商城小程序正逐步成为消费者购物的新宠儿。它不仅打破了传统电商的界限&#xff0c;更以其便捷性、高效性和个性化服务&#xff0c;重新定义了购物体验。今天&#xff0c;让我们一同探索微信商城小程序如何以独特魅力&#xff0c;引领未来…

巴鲁夫rfid读头国产平替版——高频RFID读写器

随着RFID技术的不断发展&#xff0c;国内RFID企业的数量也在不断地变多&#xff0c;国产RFID读写器的质量也越来越高。具有着价格实惠、质量可靠等特点&#xff0c;成为了可平替国外RFID产品的首要选择。健永科技的高频RFID读写器JY-H830&#xff0c;是一款可平替巴鲁夫rfid读头…

完美解决Idea中如何对Java Agent进行断点调试的方式

1、前言 在日常开发中&#xff0c;可能会存在写一个Java Agent到项目中去&#xff0c;Agent的实现可能是复杂的&#xff0c;有时候会出现attach到进程上后&#xff0c;发现没效果&#xff0c;也不知道怎么调试&#xff0c;只能通过打日志的方式实现&#xff0c;效率实在是太低…

大数据实时数仓Hologres(三):存储格式介绍

文章目录 存储格式介绍 一、格式 二、使用建议 三、技术原理 1、列存 2、行存 3、行列共存 四、使用示例 存储格式介绍 一、格式 在Hologres中支持行存、列存和行列共存三种存储格式&#xff0c;不同的存储格式适用于不同的场景。在建表时通过设置orientation属性指…

C++ bitset(位图)的模拟实现

文章目录 一、bitset接口总览二、bitset模拟实现1. 构造函数2. set、reset、flip、test3. size、count4. any、none、all5. 打印函数 三、完整代码 一、bitset接口总览 成员函数功能set设置指定位或所有位为1&#xff08;即设置为“已设置”状态&#xff09;reset清空指定位或…

华为eNSP:MAC地址安全

一、什么是MAC地址安全 MAC地址安全是一种网络安全措施&#xff0c;用于保护网络设备和通信免受未经授权的访问和潜在的安全威胁。以下是对MAC地址安全的详细介绍&#xff1a; MAC地址概述 定义&#xff1a;MAC地址&#xff08;Media Access Control Address&#xff09;是网络…

【绿豆蛙的归宿】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e510; const int M 2e510; int h[N], e[M], ne[M], idx, w[M]; double p[M]; int cnt[N]; double E; int n, m; void add(int a, int b, int c) // 添加一条边a->b {p[idx] 1, w[idx] c, e…

STM32F1+HAL库+FreeTOTS学习14——数值信号量

STM32F1HAL库FreeTOTS学习13——数值信号量 1. 数值信号量2. 相关API函数2.1 创建计数信号量2.2 获取信号量2.3 释放信号量2.4 删除信号量2.5 获取信号量的计数值 3. 操作实验1. 实验内容2. 代码实现&#xff1a;运行结果 上一期我们学习了二值信号量 &#xff0c;这一期学习计…

锅圈食品业绩承压显著:门店减少255家,押注肴肴领鲜打入农贸市场?

《港湾商业观察》廖紫雯 日前&#xff0c;锅圈食品&#xff08;上海&#xff09;股份有限公司&#xff08;以下简称&#xff1a;锅圈&#xff0c;02517.HK&#xff09;发布2024年上半年业绩运营情况。作为“在家吃饭第一股”&#xff0c;锅圈于2023年11月成功登陆港交所&#…

关于BSV区块链覆盖网络的常见问题解答(上篇)

​​发表时间&#xff1a;2024年9月20日 在BSV区块链上的覆盖网络服务为寻求可扩展、安全、高效交易处理解决方案的开发者和企业家开辟了新的视野。 作为开创性的曼达拉升级的一部分&#xff0c;覆盖网络服务提供了一个强大的框架&#xff0c;用于管理特定类型的交易和数据访问…

署名文章 | 对桂花AP2/ERFs的比较转录组分析揭示了OfERF017介导的有机酸代谢途径在花衰老中的作用

发表期刊&#xff1a;Frontiers in plant science 发表日期&#xff1a;2024年9月26日 影响因子&#xff1a;4.1 发表单位&#xff1a;湖北科技学院 研究背景 “玉露沾衣冷&#xff0c;金风拂面凉。桂花香满袖&#xff0c;秋色入诗囊。”桂花&#xff0c;中国十大名花之一…

Linux 网络配置 (深入理解)

前言 前期我比较迷惑Ubuntu 的网络配置。 我接触比较多的 Linux 发行版都是 Ubuntu &#xff0c;我按照网上的一些教程配置网络发现&#xff0c;没有相关网络配置文件夹。然后我发现不是我的问题而是不同版本的配置方式和工具是不一样的。然后有些配置已经弃用了。 常见的网络…

国庆节快乐|中国何以成为中国

华夏之土&#xff0c;广袤无垠&#xff1b;中华之史&#xff0c;源远流长。自古以来&#xff0c;中原大地物华天宝&#xff0c;人杰地灵&#xff0c;遂成一国&#xff0c;是谓中国。然中国之所以为中国&#xff0c;非徒地大物博、历史悠久也&#xff0c;更有其深厚之文化底蕴、…

实例讲解电动汽车冷却系统控制策略及Simulink建模方法

电动汽车的电机、电机控制器、DCDC、OBC在工作时都会大量发热&#xff0c;尤其是电机和电机控制器&#xff0c;功率大发热量大&#xff0c;且温度过高后会影响其正常功能&#xff0c;导致车辆故障无法正常行车&#xff0c;因此电动汽车冷却系统的控制也是一个非常重要的问题。本…