ext4 mballoc之buddy算法

news2024/12/29 5:29:18
buddy bitmap 

根据《Ext4文件系统介绍 - 理论篇_nginux的博客-CSDN博客》我们知道磁盘上有1block 大小(默认4K)data block bitmap,每bit位代表一个block的使用情况,1代表占用,0代表空闲。data block bitmap 可以表示4 * 1024 * 8 = 32768个block,32768 * 4K = 128M正好是1个block group大小。为了加速data block bitmap访问内存中同样会有一份缓存

磁盘block管理为了尽量避免碎片化问题,跟内存管理模块一样同样采用了buddy算法,同时内存构建一个buddy的bitmap,即bitmap buddy。

cat /proc/fs/ext4/xxx/mb_groups 

代码在:fs/ext4/mballoc. :
static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)

#group: free  frags first [ 2^0   2^1   2^2   2^3   2^4   2^5   2^6   2^7   2^8   2^9   2^10  2^11  2^12  2^13  ]
#0    : 18017 2     4262  [ 1     2     1     1     1     2     0     0     0     1     1     0     0     2     ]
#1    : 26581 5     2125  [ 3     3     1     1     2     1     0     1     1     1     1     0     0     3     ]
#2    : 32713 2     27    [ 3     1     3     1     1     1     0     1     1     1     1     1     1     3     ]
#3    : 32639 1     129   [ 1     1     1     1     1     1     1     0     1     1     1     1     1     3     ]
#4    : 24576 1     8192  [ 0     0     0     0     0     0     0     0     0     0     0     0     0     3     ]
#5    : 32634 3     130   [ 2     4     2     3     1     2     2     1     2     0     1     1     1     3     ]
#6    : 32749 1     19    [ 1     0     1     1     0     1     1     1     1     1     1     1     1     3     ]
#7    : 32639 1     129   [ 1     1     1     1     1     1     1     0     1     1     1     1     1     3     ]

free:代表group所有空闲block总数,该值来自于ext4_group_info.bb_free

frags :代表group连续的空闲空间段数目,该值来自于ext4_group_info.bb_fragments

first : 第一个空闲的physical block number该值来自于ext4_group_info.bb_first_free

ext4_mb_seq_groups_show:

 结合代码和上面分析我们知道打印数值来自于ext4_group_info,即通过ext4_get_group_info来获取的:

 最终ext4_group_info来自于EXT4_SB(sb) ext4_sb_info中的成员s_group_info[group],所以s_group_info成员初始化来自哪里?ext4_mb_init_cache,而是ext4_mb_load_buddy就会调用ext4_mb_init_cache,这里init cache就是指保存磁盘data block bitmap的pagecache和buddy bitmap。

ext4_mb_load_buddy->ext4_mb_init_cache->ext4_mb_generate_buddy。

根据ext4_mb_init_cache的代码注释我们知道磁盘data block bitmap和只是内存中构建的(磁盘上无对应数据,unmount时会删除)的buddy bitmap,他们各占用1个block。

 buddy bitmap格式

 

注意:

  1. 上面每个竖线代表二分
  2. 二分表示,每个区间都可以完整表示一个128M block group所有的block的使用情况,1表示占用,0表示空闲。
  3. 内核通过sbi->s_mb_offsets[]和sbi->s_mb_maxs[]两个数据记录每个区域的offset和最大的bit数量。

fs/ext4/mballoc.c : ext4_mb_init构建相应数组:

sbi->s_mb_offset[0] = 0,sbi->s_mb_maxs[1] = 32768
sbi->s_mb_offset[1] = 0,sbi->s_mb_maxs[1] = 16384
sbi->s_mb_offset[2] = 16384 sbi->s_mb_maxs[2] = 8192
...
ext4_buddy数据结构
struct ext4_buddy {
    //buddy_bitmap的page
	struct page *bd_buddy_page;
    //buddy bitmap的内存区域
	void *bd_buddy;
    //data block bitmap的内存缓存的page
	struct page *bd_bitmap_page;
	void *bd_bitmap;
	struct ext4_group_info *bd_info;
	struct super_block *bd_sb;
	__u16 bd_blkbits;
	ext4_group_t bd_group;
};

ext4_mb_load_buddy函数核心就是初始化ext4_buddy数据结构和bd_sb对应的ext4_sb_info中的s_group_info成员。注意每一个group对应一个ext4_buddy结构体。

buddy bitmap构建过程

ext4_mb_init_group和ext4_mb_load_buddy都会调用ext4_mb_init_cache,我们就以ext4_mb_init_group调用为启动分析。

static noinline_for_stack
int ext4_mb_init_group(struct super_block *sb, ext4_group_t group, gfp_t gfp)
{

	struct ext4_group_info *this_grp;
	struct ext4_buddy e4b;
	struct page *page;
	int ret = 0;

	might_sleep();
	mb_debug(sb, "init group %u\n", group);
	this_grp = ext4_get_group_info(sb, group);
    ...
	ret = ext4_mb_get_buddy_page_lock(sb, group, &e4b, gfp);
    ...
	page = e4b.bd_bitmap_page;
	ret = ext4_mb_init_cache(page, NULL, gfp);
    ...
	/* init buddy cache */
	page = e4b.bd_buddy_page;
    //传入的参数incore是e4b.bd_bitmap是磁盘中读取data block bitmap,通过这个构建buddy bitmap
	ret = ext4_mb_init_cache(page, e4b.bd_bitmap, gfp);
    ...
	return ret;
}

1.ext4_mb_get_buddy_page_lock获取group对应的data block bitmap和buddy bitmap对应的pagecache。然后page会挂在inode的高速缓存address space,这里是哪个inode?linux采用了特殊inode = 2

2.分别调用两次ext4_mb_init_cache初始化刚才创建的page缓存,第一次是data block bitmap,第二次是buddy bitmap。

ext4_mb_get_buddy_page_lock


/*
 * Lock the buddy and bitmap pages. This make sure other parallel init_group
 * on the same buddy page doesn't happen whild holding the buddy page lock.
 * Return locked buddy and bitmap pages on e4b struct. If buddy and bitmap
 * are on the same page e4b->bd_buddy_page is NULL and return value is 0.
 */
static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
		ext4_group_t group, struct ext4_buddy *e4b, gfp_t gfp)
{
	struct inode *inode = EXT4_SB(sb)->s_buddy_cache;
	int block, pnum, poff;
	int blocks_per_page;
	struct page *page;

	e4b->bd_buddy_page = NULL;
	e4b->bd_bitmap_page = NULL;

	blocks_per_page = PAGE_SIZE / sb->s_blocksize;
	/*
	 * the buddy cache inode stores the block bitmap
	 * and buddy information in consecutive blocks.
	 * So for each group we need two blocks.
	 */
	block = group * 2;
	pnum = block / blocks_per_page;
	poff = block % blocks_per_page;
	page = find_or_create_page(inode->i_mapping, pnum, gfp);
	if (!page)
		return -ENOMEM;
	BUG_ON(page->mapping != inode->i_mapping);
	e4b->bd_bitmap_page = page;
	e4b->bd_bitmap = page_address(page) + (poff * sb->s_blocksize);

	if (blocks_per_page >= 2) {
		/* buddy and bitmap are on the same page */
		return 0;
	}

	block++;
	pnum = block / blocks_per_page;
	page = find_or_create_page(inode->i_mapping, pnum, gfp);
	if (!page)
		return -ENOMEM;
	BUG_ON(page->mapping != inode->i_mapping);
	e4b->bd_buddy_page = page;
	return 0;
}

上面函数要注意pnum就是page->index,比如group =0,那么其data block bitmap对应page->index = 0, buddy bitmap对应page->index = 1。find_or_create_page如果已经存在缓存则返回,否则创建。

ext4_mb_init_cache


static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp)
{
	ext4_group_t ngroups;
	int blocksize;
	int blocks_per_page;
	int groups_per_page;
	int err = 0;
	int i;
	ext4_group_t first_group, group;
	int first_block;
	struct super_block *sb;
	struct buffer_head *bhs;
	struct buffer_head **bh = NULL;
	struct inode *inode;
	char *data;
	char *bitmap;
	struct ext4_group_info *grinfo;

	inode = page->mapping->host;
	sb = inode->i_sb;
	ngroups = ext4_get_groups_count(sb);
	blocksize = i_blocksize(inode);
	blocks_per_page = PAGE_SIZE / blocksize;

	mb_debug(sb, "init page %lu\n", page->index);

	groups_per_page = blocks_per_page >> 1;
	if (groups_per_page == 0)
		groups_per_page = 1;

	/* allocate buffer_heads to read bitmaps */
	if (groups_per_page > 1) {
		i = sizeof(struct buffer_head *) * groups_per_page;
		bh = kzalloc(i, gfp);
		if (bh == NULL) {
			err = -ENOMEM;
			goto out;
		}
	} else
		bh = &bhs;

	first_group = page->index * blocks_per_page / 2;

	/* read all groups the page covers into the cache */
	for (i = 0, group = first_group; i < groups_per_page; i++, group++) {
		if (group >= ngroups)
			break;

		grinfo = ext4_get_group_info(sb, group);
		/*
		 * If page is uptodate then we came here after online resize
		 * which added some new uninitialized group info structs, so
		 * we must skip all initialized uptodate buddies on the page,
		 * which may be currently in use by an allocating task.
		 */
		if (PageUptodate(page) && !EXT4_MB_GRP_NEED_INIT(grinfo)) {
			bh[i] = NULL;
			continue;
		}
		bh[i] = ext4_read_block_bitmap_nowait(sb, group, false);
		if (IS_ERR(bh[i])) {
			err = PTR_ERR(bh[i]);
			bh[i] = NULL;
			goto out;
		}
		mb_debug(sb, "read bitmap for group %u\n", group);
	}

	/* wait for I/O completion */
	for (i = 0, group = first_group; i < groups_per_page; i++, group++) {
		int err2;

		if (!bh[i])
			continue;
		err2 = ext4_wait_block_bitmap(sb, group, bh[i]);
		if (!err)
			err = err2;
	}

	first_block = page->index * blocks_per_page;
	for (i = 0; i < blocks_per_page; i++) {
		group = (first_block + i) >> 1;
		if (group >= ngroups)
			break;

		if (!bh[group - first_group])
			/* skip initialized uptodate buddy */
			continue;

		if (!buffer_verified(bh[group - first_group]))
			/* Skip faulty bitmaps */
			continue;
		err = 0;

		/*
		 * data carry information regarding this
		 * particular group in the format specified
		 * above
		 *
		 */
		data = page_address(page) + (i * blocksize);
		bitmap = bh[group - first_group]->b_data;

		/*
		 * We place the buddy block and bitmap block
		 * close together
		 */
		if ((first_block + i) & 1) {
			/* this is block of buddy */
			BUG_ON(incore == NULL);
			mb_debug(sb, "put buddy for group %u in page %lu/%x\n",
				group, page->index, i * blocksize);
			trace_ext4_mb_buddy_bitmap_load(sb, group);
			grinfo = ext4_get_group_info(sb, group);
			grinfo->bb_fragments = 0;
			memset(grinfo->bb_counters, 0,
			       sizeof(*grinfo->bb_counters) *
				(sb->s_blocksize_bits+2));
			/*
			 * incore got set to the group block bitmap below
			 */
			ext4_lock_group(sb, group);
			/* init the buddy */
			memset(data, 0xff, blocksize);
			ext4_mb_generate_buddy(sb, data, incore, group);
			ext4_unlock_group(sb, group);
			incore = NULL;
		} else {
			/* this is block of bitmap */
			BUG_ON(incore != NULL);
			mb_debug(sb, "put bitmap for group %u in page %lu/%x\n",
				group, page->index, i * blocksize);
			trace_ext4_mb_bitmap_load(sb, group);

			/* see comments in ext4_mb_put_pa() */
			ext4_lock_group(sb, group);
			memcpy(data, bitmap, blocksize);

			/* mark all preallocated blks used in in-core bitmap */
			ext4_mb_generate_from_pa(sb, data, group);
			ext4_mb_generate_from_freelist(sb, data, group);
			ext4_unlock_group(sb, group);

			/* set incore so that the buddy information can be
			 * generated using this
			 */
			incore = data;
		}
	}
	SetPageUptodate(page);

out:
	if (bh) {
		for (i = 0; i < groups_per_page; i++)
			brelse(bh[i]);
		if (bh != &bhs)
			kfree(bh);
	}
	return err;
}

读取磁盘中的data block bitmap,初始化刚刚创建的page cache,这个逻辑比较简单,只是将磁盘中的bitmap赋值给page cache。复杂的时候初始化只在内存中的buddy bitmap,这是通过ext4_mb_generate_buddy,这个地方要注意buddy bitmap开始默认都初始化为1,代表占用状态,ext4_mb_generate_buddy通过bd_bitmap构建出来buddy bitmap

 max:group最大的cluster数量(如果block size = cluster size就是block数量)

i = mb_find_next_zero_bit(bitmap, max, 0);根据data block bitmap找到第一个空闲的位置返回给i。

mb_find_next_bit(bitmap, max, i)找到下一给1的bit,那么len = i - first就是一段连续空闲的长度。

ext4_mb_mark_free_simple将这段连续的空闲区域记录到buddy bitmap中。

假设有个block group从129 block之后全部是空闲,ext4_mb_mark_free_simple的执行流程:

32639 chunk = 1
first = 129

第一轮:first = 129 len = 32639 
	bb_counters[0]++
第二轮:   = 130 len = 32638 
	max = ffs(130) - 1 = 1
	min = fls(32638) - 1 = 14
	min = 1; chunk = 1 << min = 2
	
	bb_counters[1]++;
	
第三轮:first = 132 len = 32636
	max = ffs(132) - 1= 2 
	min = fls(32636) - 1= 14
	min = 2; chunk = 1 << min = 4
	bb_counters[2]++;
	
4: first = 136 len = 32632
	max = ffs(136) -1 = 3
	min = fls(32632) - 1=  14
	min = 3 chunk = 1 << 3 = 8
	bb_counters[3]++;
	
5: first = 144 len = 32624
	max = ffs(144) - 1 = 4
	min = fls(32624) - 1= 14
	min = 4 chunk = 16
	bb_counter[4]++
	
6: first = 160 len = 32608
	max = ffs(160) -1 = 5
	min = fls(32608) - 1 = 14
	min = min (max, min) = 5
	chunk = 1 << min = 1 << 5 = 32
	bb_counter[5]++
	
7: fisrt = 192 len = 32576
   max = ffs(192) - 1 = 6
   min = fls(32576) -1 = 14
	min = min(max, min) =6
	chunk = 1 << min = 64
	bb_counters[6]++
	
8: first = 256	len = 32512
	max = ffs(256) - 1 = 8
	min = fls(32512) - 1= 14
	min = min(max, min) = 8
	chunk = 1 << 8 = 256
	bb_counters[8]++
	
9: first = 512 len = 32256
	max = ffs(512) -1 = 9
	min = fls(32256) - 1 = 14
	min = min(max, min) = 9
	chunk = 1 << 9 = 512
	bb_counters[9]++

10: first = 1024 len = 31744
	max = ffs(1024) - 1 = 10
	min = fls(31744) - 1 = 14
	min = min(max, min) = 10
	chunk = 1 << 10 = 1024
	bb_counters[10]++
	
11:	first = 2048 len = 30720
	max = ffs(2048) -1 = 11
	min = fls(30720)-1 = 14
	min = min(max, min) = 11
	chunk  = 1 << 11= 2048
	bb_counters[11]++;
	
12: first = 4096 len = 26624
	max = ffs(4096) - 1 = 12
	min = fls(26624) - 1 = 14
	min = min(max,min) = 12
	chunk = 1 << 12 = 4096
	bb_counters[12]++
	
13: first = 8192 len = 24576
	max = ffs(8192) -1 = 13
	min = fls(24576) -1 = 14
	min = min(max,min) = 13
	chunk = 1 << 13 = 8192
	bb_counters[13]++
	
14: first = 16384 len = 16384
	max = ffs(16384|border) -1 = 13
	min = fls(16384) - 1= 14
	min = min(max, min) = 13
	chunk = 1 << 13 = 8192
	bb_counter[13]++
	
14: first = 24576 len = 8192
	max = ffs(24576|border) -1 = 13
	min = fls(8192) -1 = 13
	min = min(max ,min) = 13
	chunk = 1 << 13 = 8192
	bb_counters[13]++
	
15: first = 32768 len = 0

 最终引用google的一个PPT中图示 :

 

参考文章:

关于ext4 buddy bitmap构建分析_ext4 ext4_buddy_五年一剑的博客-CSDN博客

https://www.cnblogs.com/kanie/p/15359346.html

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

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

相关文章

代码随想录| 图论02●695岛屿最大面积 ●1020飞地的数量 ●130被围绕的区域 ●417太平洋大西洋水流问题

#695岛屿最大面积 模板题&#xff0c;很快.以下两种dfs&#xff0c;区别是看第一个点放不放到dfs函数中处理&#xff0c;那么初始化的area一个是1一个是0 int dir[4][2]{0,1,0,-1,1,0,-1,0};void dfs(int x, int y,int n, int m, int &area,vector<vector<bool>…

HTML入门教程||HTML 属性||HTML 元素

HTML 元素 HTML 元素 HTML 文档由 HTML 元素定义&#xff0c;HTML 元素指的是从开始标签&#xff08;start tag&#xff09;到结束标签&#xff08;end tag&#xff09;的所有代码。 HTML 元素 开始标签 *元素内容结束标签 *<p>这是一个段落</p><a href"…

《Docker和服务器无状态化:容器化应用的优势,构建高可伸缩性和灵活性》

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【MATLAB第58期】基于MATLAB的PCA-Kmeans、PCA-LVQ与BP神经网络分类预测模型对比

【MATLAB第58期】基于MATLAB的PCA-Kmeans、PCA-LVQ与BP神经网络分类预测模型对比 一、数据介绍 基于UCI葡萄酒数据集进行葡萄酒分类及产地预测 共包含178组样本数据&#xff0c;来源于三个葡萄酒产地&#xff0c;每组数据包含产地标签及13种化学元素含量&#xff0c;即已知类…

c++高性能264/265实时流媒体服务器/h5客户端整体解决方案源码

文章目录 c高性能264/265实时流媒体服务器/h5客户端整体解决方案源码缘由目前的前端技术栈&#xff0c;已经能够支撑常规的安防桌面客户端软件开发我的方案一套c后端,两套前端H5 UI方案一&#xff1a;多屏h265/h264混合显示H5 UI方案二&#xff1a;H5监控大屏,提供视图切换功能…

基于SRS后端开发的前端页面

SRS(Simple Realtime Server)是简单高效的实时视频服务器&#xff0c;支持RTMP、WebRTC、HLS、HTTP-FLV、SRT等多种实时流媒体协议。 框架 https://www.thinkphp.cn 官网 https://ossrs.net/lts/zh-cn 播放器1 https://www.cdnbye.com 播放器2 https://www.artplayer.org …

苹果的Apple GPT要来了?

据外媒消息&#xff0c;苹果正在内部开发类 ChatGPT 的产品&#xff0c;与微软、OpenAI、谷歌、Meta 等科技巨头在生成式 AI 赛道展开竞争。该消息使得苹果股价上涨了 2%。据苹果工程师透露&#xff0c;苹果在内部构建了代号为“Ajax”的大语言模型开发框架&#xff0c;并构建了…

【C语言】表达式求值相关问题汇总—>隐式类型转换(整型提升)、算数转换与操作符优先级汇总(收藏查阅)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负。 目录 前言&#xff1a; 一、隐式类型转换 &#xff08;一&#xff09;整型提升的意义…

【六天】高时空分辨率、高精度一体化预测技术之风、光、水能源自动化预测技术应用

最初的互补发电系统&#xff0c;就是将不同发点组件进行简单的组合&#xff0c;因为缺乏详细的数学计算模型&#xff0c;同时系统只用于保证率低的用户&#xff0c;导致使用寿命不长。随着新能源系统应用范围的不断扩大&#xff0c;保证率和经济性要求的提高&#xff0c;需要高…

Rust vs Go:常用语法对比(四)

题图来自 Go vs. Rust performance comparison: The basics 61. Get current date 获取当前时间 package mainimport ( "fmt" "time")func main() { d : time.Now() fmt.Println("Now is", d) // The Playground has a special sandbox, so you …

【力扣周赛】第 354 场双周赛

文章目录 Q1&#xff1a;2784. 检查数组是否是好的解法1——排序模拟判断解法2——哈希表计数模拟判断 Q2&#xff1a;6926. 将字符串中的元音字母排序Q3&#xff1a;6931. 访问数组中的位置使分数最大&#xff08;线性DP&#xff09;Q4&#xff1a;6922. 将一个数字表示成幂的…

什么是神经网络?

我们常常使用深度学习来指训练神经网络的过程。 在这里举一个房屋价格预测的例子&#xff1a;假设有一个数据集&#xff0c;它包含了六栋房子的信息。所以&#xff0c;你知道房屋的面积是多少平方米&#xff0c;并且知道这个房屋的价格。这是&#xff0c;你想要拟合一个根据房屋…

【Linux】linux工具和命令

这里写目录标题 一、Linux常用命令&#xff1a;二、Linux安装软件&#xff1a;1.yum安装2.Linux和Windows文件互传3.yum卸载软件 三、vim编辑器1.命令模式2.vim配置项说明3.vim操作总结 一、Linux常用命令&#xff1a; ls 显示当前目录下的文件 ls-a 显示当前目录下所有文件&a…

自定义类型:结构体进阶学习分享

自定义类型&#xff1a;结构体进阶学习分享 前言1 结构体的基础知识2 结构的声明3 特殊声明4 结构的自引用5 结构体变量的定义和初始化6 结构体内存对齐6.1 计算结构体大小相关笔试题&#xff08;基于VS&#xff09;笔试题一&#xff1a;笔试题二&#xff1a; 6.2 为什么存在内…

【C语言】指针进阶(1)

在前期的文章中&#xff0c;我们已经学习完了指针初阶的内容&#xff0c;这期我们开始学习指针的进阶部分。 指针初阶文章入口&#xff1a; 指针初阶 目录 重点知识概览 前期回顾 字符指针 指针数组 数组指针 数组指针的定义 &数组名VS数组名 数组指针的使用 数组…

Mac电脑文件夹无权限问题

sudo cp 16.5.zip /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 走到之前的folder &#xff0c;右键选择get info更改權限, 再應用到所有子文件夹 右下解鎖再加自己Read & Write, -右邊拉下應該可以應用到所有子文件 这样就可以…

Java ~ Executor ~ ExecutorCompletionService【总结】

前言 文章 相关系列&#xff1a;《Java ~ Executor【目录】》&#xff08;持续更新&#xff09;相关系列&#xff1a;《Java ~ Executor ~ ExecutorCompletionService【源码】》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;相关系列&#xff1a;《Java ~ Exe…

如何做需求分析

目录 核心理念&#xff1a; 主要目的&#xff1a; 具体思路&#xff1a; 注意事项&#xff1a; 核心理念&#xff1a; 首先需要想清楚一个问题&#xff1a;作为一个测试&#xff0c;有没有把需求当作产品中的一个组成部分&#xff0c;然后尽到一个测试的责任与义务&#x…

JavaScript中truthy(真值)或者Falsy(假值)

● 在JavaScript中&#xff0c;有五个值是falsy ○ 0 ○ ’ ’ ○ undefined ○ null ○ NaN 除此之外&#xff0c;任何不是空值的都是真值&#xff1b; 假值是什么意思呢&#xff1f;就是转换为布尔值都是false&#xff0c;反则就是true 例如&#xff1a; console.log(Boole…

论文阅读:矩阵乘法GEMM的cache优化,子矩阵的切分方法Anatomy of High-Performance MatrixMultiplication

矩阵乘法优化的知名论文goto paper&#xff1a; 矩阵乘法的优化需要将矩阵切分成子矩阵&#xff0c;用子矩阵相乘的结果组合为原矩阵相乘的结果&#xff1a; 上图是拆分矩阵的方法&#xff0c;M表示矩阵&#xff0c;X方向和Y方向的两个维度都是未知的。P表示横条或竖条&#x…