首页 > 其他分享 >CSAPP:Cache模拟器

CSAPP:Cache模拟器

时间:2024-07-30 22:59:25浏览次数:14  
标签:StoreValue BlockOffset CSAPP CacheLineAddress Cache PER DCache Data 模拟器

实验要求

  • 实现一个组相联的Data Cache,达到最高命中率
  • Data Cache最大只能使用16KB的数据存储空间
  • 附加功能:实现指令Cache

分析

  • 实验的框架代码已经给出(直接映射),要改成组相联的结构,无非就是修改访问Data Cache的接口,确定组号后遍历组内每一行的Tag,若能与Address匹配上则hit,否则miss
  • 以上内容任何一个人写出来都一样,关键是如何采用合适的策略提高Cache的命中率?
  • 根据教材6.4.7的总结,我考虑了从以下几个方面来优化:
    • 高速缓存大小(C):毫无疑问较大的Cache大小可以存下更多的数据,显著减少行替换,因此这里采用16KB
    • 组数(S)、相联度(E)、块大小(B):以上三个指标的选择没有理论指导,在后续实验中通过不断调整测试找出最优方案
    • 替换算法:实验中先后尝试了固定替换法、LRU法(最近最少使用,Least Recently Used)、随机法,从实验结果上看,命中率:随机法>固定替换法>LRU法
  • 写策略:不同的写策略对命中率没有影响,但很明显写回策略比直写策略耗时更短,因此实验中采用写回策略

实验过程

实验开始前,先运行框架代码,得到直接相联的命中率为84.95%,记录下来以便比较
注:这里及后续实验记录的都是运行./Cache traces/gedit.trace.zst这一测试集的结果

策略一:固定替换法

  • 首先解析Address得到标记、组索引和块偏移,然后在组内遍历每一行,若能找到Valid为1且Tag与之匹配的行,则命中(H),否则不命中(M
  • 若命中,则直接根据操作类型进行对应的操作(这部分代码几乎没有改动),操作完后直接break
    • 注意若执行了写或修改,需要将结构体中的Dirty(脏位)设置为1,方便后续写回
  • 若不命中,则在组内遍历,若能找到空行(即Valid为0),则直接将数据Load到这一行,并设置好相应的控制位;否则再次遍历,若能找到脏位为0的(即未经过修改的)行,则直接将数据Load到这一行,并设置好相应的控制位(因为这样的行无需写回,可以节省时间);若上述两次遍历后还是没有找到符合要求的行,则说明组内每一行Valid都为1,且都做过修改,替换时需要写回,那么就固定取第0行:首先从Tag中恢复旧的地址(OldAddress),将Cache中的内容写回后Load新数据到这一行。随后,根据操作类型进行对应的操作
  • 实验结果如下表所示
    • 改成组相联后命中率比直接相联更低,那么替换策略改成LRU法会是什么效果呢?
(S,E,B)命中率(%)
(32,8,64)76.73
(16,16,64)73.49
(32,16,32)70.16
(64,8,32)64.58

策略二:LRU法

  • 在每一行增加了一个变量Time用于记录这一行有多少次被遍历到,但Tag没有匹配上
    • 初始时Time为0,一旦匹配上Time清零,否则Time自增1
    • 在替换时只需找出Time最大的行进行替换即可
  • 其余部分未做大的变动,不再赘述
  • 实验结果如下表所示
    • 和上述策略相比,LRU法的命中率反而有所下降。并且,可以看出,相联度较低时,命中率反而越高。当相联度为1时,命中率最高,但这不就是直接相联了吗?
(S,E,B)命中率(%)
(32,8,64)72.19
(16,16,64)68.18
(32,4,128)76.83
(64,2,128)81.95

策略三:随机法

  • 大道至简,返璞归真
  • 随机法的策略就是在组内已满的情况下,用rand()随机选取一行进行替换,这可比实现LRU简单多了
  • 实验结果如下表所示
    • 显然随机法的效果是最好的,最优方案是(S,E,B)=(16,64,16),命中率达到93.58%,比框架代码(直接相联)的命中率提高了10.2%
    • 并且可以看出,在一定范围内,相联度越高,命中率越高,命中率最高点绝不是直接相联
(S,E,B)命中率(%)
(32,8,64)88.99
(64,4,64)88.53
(64,2,128)86.83
(32,16,32)92.09
(32,32,16)93.39
(16,64,16)93.58
(16,128,8)93.50
(8,256,8)93.54
(2,1028,8)93.56

附加功能:实现Instruction Cache

  • 实现的思路和DCache类似,并且由于指令Cache只读,即Operation一定为I,所以实现起来更简单
  • 根据上面的实验结果,直接选择了更易实现且效果更好的随机替换策略
  • (S,E,B)=(16,64,16)条件下,命中率达到98.87%

实验结果

Data Cache命中率:93.58%
Inst Cache命中率:98.87%

实验结果讨论

  • 为什么随机法这一玄学策略的效果比更讲科学的LRU法效果更好,我查询了一些资料,得出了如下结论:
    • 数据访问模式:如果数据的访问模式非常随机,没有任何可预测的模式,那么随机算法可能表现得和更复杂的算法一样好,甚至更好。因为LRU算法是基于“最近最少使用的数据将来也不太可能被访问”的假设,如果访问模式完全随机,这个假设就不成立
    • 预取和局部性原理:LRU算法利用了计算机科学中的局部性原理,即最近访问过的数据很可能在不久的将来再次被访问。然而,如果测试集中数据访问模式违背了这一原理,LRU算法的优势就无法体现
    • 适应性:LRU算法需要一定时间来“学习”或“适应”访问模式,对于变化非常快的访问模式,LRU可能还没有适应新的模式,就已经需要进行替换决策了
  • 为什么要将数据和指令单独放在两个Cache里?
    • 提高性能:由于数据和指令可以同时被访问(实现并行),这可以减少CPU等待时间,从而提高整体性能
    • 专用优化:可以针对数据和指令的不同特性进行专门的优化,比如数据访问可能更倾向于连续性,而指令访问可能更随机(当然在这个实验的测试集下都挺随机的)
    • 安全性:在某些系统中,将数据和指令分开可以提高安全性,防止某些类型的攻击,如缓冲区溢出攻击

完整代码

///
  Copyright 2024 by Lane.                                        //
///

#include <stdio.h>
#include <stdlib.h>

#include <time.h>

#include "common.h"

#define DEBUG	0

#define GET_POWER_OF_2(X)	(X == 0x00		? 0 : \
							X == 0x01		? 0 : \
							X == 0x02		? 1 : \
							X == 0x04		? 2 : \
							X == 0x08		? 3 : \
							X == 0x10		? 4 : \
							X == 0x20		? 5 : \
							X == 0x40		? 6 : \
							X == 0x80		? 7 : \
							X == 0x100		? 8 : \
							X == 0x200		? 9 : \
							X == 0x400		? 10 : \
							X == 0x800		? 11 : \
							X == 0x1000		? 12 : \
							X == 0x2000		? 13 : \
							X == 0x4000		? 14 : \
							X == 0x8000		? 15 : \
							X == 0x10000	? 16 : \
							X == 0x20000	? 17 : \
							X == 0x40000	? 18 : \
							X == 0x80000	? 19 : \
							X == 0x100000	? 20 : \
							X == 0x200000	? 21 : \
							X == 0x400000	? 22 : \
							X == 0x800000	? 23 : \
							X == 0x1000000	? 24 : \
							X == 0x2000000	? 25 : \
							X == 0x4000000	? 26 : \
							X == 0x8000000	? 27 : \
							X == 0x10000000	? 28 : \
							X == 0x20000000	? 29 : \
							X == 0x40000000	? 30 : \
							X == 0x80000000	? 31 : \
							X == 0x100000000	? 32 : 0)	// 例如:2^32=0x100000000

/*
	直接映射Data Cache,16KB大小
	每行存放64个字节,共32组,每组8行
*/
#define DCACHE_SIZE						16384
#define DCACHE_DATA_PER_LINE			16										// DCache每行存放字节数,必须是8字节的倍数
#define DCACHE_DATA_PER_LINE_ADDR_BITS	GET_POWER_OF_2(DCACHE_DATA_PER_LINE)	// 必须与上面设置一致
#define DCACHE_SET						16										// DCache组数
#define DCACHE_SET_ADDR_BITS			GET_POWER_OF_2(DCACHE_SET)				// 必须与上面设置一致
#define DCACHE_LINES_PER_SET			(DCACHE_SIZE/DCACHE_DATA_PER_LINE/DCACHE_SET)	// 每组行数
#define DCACHE_LINES					DCACHE_SIZE/DCACHE_DATA_PER_LINE		// DCache行数

#define ICACHE_SIZE						16384
#define ICACHE_DATA_PER_LINE			16										// ICache每行存放字节数,必须是8字节的倍数
#define ICACHE_DATA_PER_LINE_ADDR_BITS	GET_POWER_OF_2(ICACHE_DATA_PER_LINE)	// 必须与上面设置一致
#define ICACHE_SET						16										// ICache组数
#define ICACHE_SET_ADDR_BITS			GET_POWER_OF_2(ICACHE_SET)				// 必须与上面设置一致
#define ICACHE_LINES_PER_SET			(ICACHE_SIZE/ICACHE_DATA_PER_LINE/ICACHE_SET)	// 每组行数
#define ICACHE_LINES					ICACHE_SIZE/ICACHE_DATA_PER_LINE		// ICache行数


// Cache行的结构,包括Valid、Tag和Data。你所有的状态信息,只能记录在Cache行中!
struct DCACHE_LineStruct
{
	UINT8	Valid;
	UINT8 	Dirty;	// 脏位
	UINT64	Time;	// 时间戳
	UINT64	Tag;
	UINT8	Data[DCACHE_DATA_PER_LINE];
}DCache[DCACHE_LINES];

struct ICACHE_LineStruct
{
	UINT8	Valid;
	UINT64	Tag;
	UINT8	Data[ICACHE_DATA_PER_LINE];
}ICache[ICACHE_LINES];

/*
	DCache初始化代码,一般需要把DCache的有效位Valid设置为0
	模拟器启动时,会调用此InitDataCache函数
*/
void InitDataCache()
{
	UINT32 i;
	printf("[%s] +-----------------------------------+\n", __func__);
	printf("[%s] |   Lane的Data Cache初始化ing....   |\n", __func__);
	printf("[%s] +-----------------------------------+\n", __func__);
	for (i = 0; i < DCACHE_LINES; i++)
	{
		DCache[i].Valid = 0;
		DCache[i].Dirty = 0;
		DCache[i].Time = 0;
	}
}

/*
	从Memory中读入一行数据到Data Cache中
*/
void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress)
{
	// 一次性从Memory中将DCACHE_DATA_PER_LINE数据读入某个Data Cache行
	UINT32 i;
	UINT64 ReadData;
	UINT64 AlignAddress;
	UINT64* pp;

	AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1);	// 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界
	pp = (UINT64*)DCache[CacheLineAddress].Data;
	for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++)
	{
		ReadData = ReadMemory(AlignAddress + 8LL * i);
		pp[i] = ReadData;
		if (DEBUG)
			printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, ReadData);
	}
}

/*
	将Data Cache中的一行数据,写入存储器
*/
void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineAddress)
{
	// 一次性将DCACHE_DATA_PER_LINE数据从某个Data Cache行写入Memory中
	// 提供了一个函数,一次可以写入8个字节
	UINT32 i;
	UINT64 WriteData;
	UINT64 AlignAddress;
	UINT64* pp;

	AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1);	// 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界
	pp = (UINT64*)DCache[CacheLineAddress].Data;
	WriteData = 0;
	for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++)
	{
		WriteData = pp[i];
		WriteMemory(AlignAddress + 8LL * i, WriteData);
		if (DEBUG)
			printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, WriteData);
	}
}

/*
	Data Cache访问接口,系统模拟器会调用此接口,来实现对你的Data Cache访问
	Address:	访存字节地址
	Operation:	操作:读操作('L')、写操作('S')、读-修改-写操作('M')
	DataSize:	数据大小:1字节、2字节、4字节、8字节
	StoreValue:	当执行写操作的时候,需要写入的数据
	LoadResult:	当执行读操作的时候,从Cache读出的数据
*/
UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, UINT64 StoreValue, UINT64* LoadResult)
{
	UINT32 CacheSetAddress;
	UINT32 CacheLineAddress;
	UINT8 BlockOffset;
	UINT64 AddressTag;
	UINT8 MissFlag = 'M';
	UINT64 ReadValue;

	*LoadResult = 0;

	/*
	*	组相联中,Address被切分为  AddressTag,CacheSetAddress,BlockOffset
	*/

	CacheSetAddress = (Address >> DCACHE_DATA_PER_LINE_ADDR_BITS) % DCACHE_SET;	// 组索引
	BlockOffset = Address % DCACHE_DATA_PER_LINE;	// 块偏移
	AddressTag = (Address >> DCACHE_DATA_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS;	// 地址去掉DCACHE_SET、DCACHE_DATA_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!!

	for(int i=0;i<DCACHE_LINES_PER_SET;i++)
	{
		CacheLineAddress=CacheSetAddress*DCACHE_LINES_PER_SET+i;
		if (MissFlag == 'M' && DCache[CacheLineAddress].Valid == 1 && DCache[CacheLineAddress].Tag == AddressTag)
		{
			MissFlag = 'H';		// 命中!
			DCache[CacheLineAddress].Time=0;
			if (Operation == 'L')	// 读操作
			{
				ReadValue = 0;
				switch (DataSize)
				{
				case 1:	// 1个字节
					ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 0];
					break;
				case 2:	// 2个字节
					BlockOffset = BlockOffset & 0xFE;	// 需对齐到2字节边界
					ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 0];
					break;
				case 4:	// 4个字节
					BlockOffset = BlockOffset & 0xFC;	// 需对齐到4字节边界
					ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 0];
					break;
				case 8:	// 8个字节
					BlockOffset = BlockOffset & 0xF8;	// 需对齐到8字节边界
					ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 7]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 6]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 5]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 4]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
					ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 0];
					break;
				}
				*LoadResult = ReadValue;
				if (DEBUG)
					printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX ReadValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue, ReadValue);
			}
			else if (Operation == 'S' || Operation == 'M')	// 写操作(修改操作在此等价于写操作)
			{
				if (DEBUG)
					printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue);
				DCache[CacheLineAddress].Dirty=1;
				switch (DataSize)
				{
				case 1:	// 1个字节
					DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF;
					break;
				case 2:	// 2个字节
					BlockOffset = BlockOffset & 0xFE;	// 需对齐到2字节边界
					DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF;
					break;
				case 4:	// 4个字节
					BlockOffset = BlockOffset & 0xFC;	// 需对齐到4字节边界
					DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF;
					break;
				case 8:	// 8个字节
					BlockOffset = BlockOffset & 0xF8;	// 需对齐到8字节边界
					DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
					DCache[CacheLineAddress].Data[BlockOffset + 7] = StoreValue & 0xFF;
					break;
				}
			}
		}
		else if(DCache[CacheLineAddress].Valid == 1)
			DCache[CacheLineAddress].Time++;
	}

	if(MissFlag == 'M')		// 不命中
	{
		UINT8 OKFlag=0;
		if (DEBUG)
			printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue);	
		// 该组是否full
		for(int i=0;i<DCACHE_LINES_PER_SET;i++)
		{
			CacheLineAddress=CacheSetAddress*DCACHE_LINES_PER_SET+i;
			if(DCache[CacheLineAddress].Valid == 0)
			{
				OKFlag=1;
				break;
			}
		}
		// 不是full则load到CacheLineAddress里,否则替换Time最大的
		if(OKFlag==0)
		{
			// UINT64 Time_max=-1;
			// UINT32 CacheLineAddress_tmp;
			// for(int i=0;i<DCACHE_LINES_PER_SET;i++)
			// {
			// 	CacheLineAddress_tmp=CacheSetAddress*DCACHE_LINES_PER_SET+i;
			// 	if(DCache[CacheLineAddress_tmp].Time>Time_max)
			// 	{
			// 		Time_max=DCache[CacheLineAddress_tmp].Time;
			// 		CacheLineAddress=CacheLineAddress_tmp;
			// 	}
			// }
			// CacheLineAddress=CacheSetAddress*DCACHE_LINES_PER_SET;
			// 生成一个随机数
			UINT32 random_number = rand() % DCACHE_LINES_PER_SET;
			CacheLineAddress=CacheSetAddress*DCACHE_LINES_PER_SET+random_number;
		}

		if(DCache[CacheLineAddress].Valid==1 && DCache[CacheLineAddress].Dirty==1)
		{
			// 淘汰对应的Cache行,如果对应的Cache行有数据,需要写回到Memory中
			UINT64 OldAddress;
			// OldAddress = > (Tag,Set,000000)
			OldAddress = ((DCache[CacheLineAddress].Tag << DCACHE_SET_ADDR_BITS) << DCACHE_DATA_PER_LINE_ADDR_BITS) | ((UINT64)CacheSetAddress << DCACHE_DATA_PER_LINE_ADDR_BITS);	// 从Tag中恢复旧的地址
			StoreDataCacheLineToMemory(OldAddress, CacheLineAddress);
		}
		
		LoadDataCacheLineFromMemory(Address, CacheLineAddress);
		DCache[CacheLineAddress].Valid = 1;
		DCache[CacheLineAddress].Tag = AddressTag;
		DCache[CacheLineAddress].Dirty = 0;
		DCache[CacheLineAddress].Time = 0;
		
		if (Operation == 'L')	// 读操作
		{
			// 读操作不需要做事情,因为已经MISS了
		}
		else if (Operation == 'S' || Operation == 'M')	// 写操作(修改操作在此等价于写操作)
		{
			DCache[CacheLineAddress].Dirty=1;
			// 写操作,需要将新的StoreValue更新到CacheLine中
			switch (DataSize)
			{
			case 1:	// 1个字节
				DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF;
				break;
			case 2:	// 2个字节
				BlockOffset = BlockOffset & 0xFE;	// 需对齐到2字节边界
				DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF;
				break;
			case 4:	// 4个字节
				BlockOffset = BlockOffset & 0xFC;	// 需对齐到4字节边界
				DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF;
				break;
			case 8:	// 8个字节
				BlockOffset = BlockOffset & 0xF8;	// 需对齐到8字节边界
				DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8;
				DCache[CacheLineAddress].Data[BlockOffset + 7] = StoreValue & 0xFF;
				break;
			}
		}
	}
	return MissFlag;
}

/* 指令Cache实现部分,可选实现 */
void InitInstCache(void)
{
	UINT32 i;
	printf("[%s] +-----------------------------------+\n", __func__);
	printf("[%s] |Lane的Instruction Cache初始化ing....|\n", __func__);
	printf("[%s] +-----------------------------------+\n", __func__);
	for (i = 0; i < ICACHE_LINES; i++)
	{
		ICache[i].Valid = 0;
	}
}

void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress)
{
	// 一次性从Memory中将ICACHE_DATA_PER_LINE数据读入某个ICache行
	UINT32 i;
	UINT64 ReadData;
	UINT64 AlignAddress;
	UINT64* pp;

	AlignAddress = Address & ~(ICACHE_DATA_PER_LINE - 1);	// 地址必须对齐到ICACHE_DATA_PER_LINE (64)字节边界
	pp = (UINT64*)ICache[CacheLineAddress].Data;
	for (i = 0; i < ICACHE_DATA_PER_LINE / 8; i++)
	{
		ReadData = ReadMemory(AlignAddress + 8LL * i);
		pp[i] = ReadData;
	}
}

UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, UINT64* InstResult)
{
	// 返回值'M' = Miss,'H'=Hit
	// Operation一定是'I',只读

	UINT32 CacheSetAddress;
	UINT32 CacheLineAddress;
	UINT8 BlockOffset;
	UINT64 AddressTag;
	UINT8 MissFlag = 'M';
	UINT64 ReadValue;

	*InstResult = 0;

	/*
	*	组相联中,Address被切分为  AddressTag,CacheSetAddress,BlockOffset
	*/

	CacheSetAddress = (Address >> ICACHE_DATA_PER_LINE_ADDR_BITS) % ICACHE_SET;	// 组索引
	BlockOffset = Address % ICACHE_DATA_PER_LINE;	// 块偏移
	AddressTag = (Address >> ICACHE_DATA_PER_LINE_ADDR_BITS) >> ICACHE_SET_ADDR_BITS;	// 地址去掉ICACHE_SET、ICACHE_DATA_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!!

	for(int i=0;i<ICACHE_LINES_PER_SET;i++)
	{
		CacheLineAddress=CacheSetAddress*ICACHE_LINES_PER_SET+i;
		if (ICache[CacheLineAddress].Valid == 1 && ICache[CacheLineAddress].Tag == AddressTag)
		{
			MissFlag = 'H';		// 命中!
			ReadValue = 0;
			switch (InstSize)
			{
			case 1:	// 1个字节
				ReadValue = ICache[CacheLineAddress].Data[BlockOffset + 0];
				break;
			case 2:	// 2个字节
				BlockOffset = BlockOffset & 0xFE;	// 需对齐到2字节边界
				ReadValue = ICache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 0];
				break;
			case 4:	// 4个字节
				BlockOffset = BlockOffset & 0xFC;	// 需对齐到4字节边界
				ReadValue = ICache[CacheLineAddress].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 0];
				break;
			case 8:	// 8个字节
				BlockOffset = BlockOffset & 0xF8;	// 需对齐到8字节边界
				ReadValue = ICache[CacheLineAddress].Data[BlockOffset + 7]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 6]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 5]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 4]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 3]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 2]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8;
				ReadValue |= ICache[CacheLineAddress].Data[BlockOffset + 0];
				break;
			}
			*InstResult = ReadValue;
		}
	}

	if(MissFlag == 'M')		// 不命中
	{
		UINT8 OKFlag=0;
		// 该组是否full
		for(int i=0;i<ICACHE_LINES_PER_SET;i++)
		{
			CacheLineAddress=CacheSetAddress*ICACHE_LINES_PER_SET+i;
			if(ICache[CacheLineAddress].Valid == 0)
			{
				OKFlag=1;
				break;
			}
		}
		if(OKFlag==0)
		{
			// 生成一个随机数
			UINT32 random_number = rand() % ICACHE_LINES_PER_SET;
			CacheLineAddress=CacheSetAddress*ICACHE_LINES_PER_SET+random_number;
		}

		// printf("hello!\n");
		LoadInstCacheLineFromMemory(Address, CacheLineAddress);
		ICache[CacheLineAddress].Valid = 1;
		ICache[CacheLineAddress].Tag = AddressTag;
	}
	return MissFlag;
}

标签:StoreValue,BlockOffset,CSAPP,CacheLineAddress,Cache,PER,DCache,Data,模拟器
From: https://blog.csdn.net/Lane0218/article/details/140808501

相关文章

  • java @Cacheable生成的redisKey,出现两个连续的冒号::
    1、参考基于redis2.1.6实现springcache生成的key多出一个冒号2、解决需要对key进行处理,【重点】是computePrefixWith方法config=config.computePrefixWith(cacheName->{returncacheName+StrUtil.COLON;});以下是完整代码实现CacheK......
  • CSAPP(一)
    0x01关于补码iHATE公式对于char类型2Byte,8位,其中有1位视为符号位表示最大值2^72^62^52^42^32^22^12^001111111UMax:-2^7*0+2^6+2^5+2^4+2^3+2^2+2^1+1=127转为16进制表示即:7FTMax:-2^7+2^6+2^5+2^4+2^3+2^2+2......
  • Web 安全:Memcached 未授权访问漏洞.(11211端口)
    Web安全:Memcached未授权访问漏洞Memcached是一套常用的key-value缓存系统,由于它本身没有权限控制模块,所以对公网开放的Memcache服务很容易被攻击者扫描发现。然而Memcached的默认配置,11211端口 不需要密码即可访问,可以直接连接到Memcached服务的11211端口获取......
  • 高并发内存池(五)Thread Cache、Central Cache回收功能的实现
    目录一、ThreadCache的回收实现1.1ThreadCache回收框架1.2ThreadCache回收实现二、CentralCache2.1CentralCache回收框架2.2CentralCache回收实现一、ThreadCache的回收实现1.1ThreadCache回收框架在实现完整的高并发内存池内存分配逻辑以后,回收逻辑就变得......
  • Memcached跨平台性能解码:操作系统对缓存速度的影响
    Memcached跨平台性能解码:操作系统对缓存速度的影响在分布式缓存系统的设计和部署中,Memcached因其轻量级和高性能而成为首选方案之一。然而,Memcached在不同操作系统上的性能表现可能会有显著差异。本文将深入探讨这些差异的原因,并提供实际的测试方法和代码示例,帮助系统架构......
  • 探索Memcached的宇宙:APIs及其工作原理深度解析
    ......
  • 深度解析Memcached:内存分配算法的优化之旅
    ......
  • 计算机组成与体系结构-cache
    概念Cache,高速缓冲存储器。是解决CPU与主存之间速度不匹配而采用的一项重要技术,位于主存与CPU之间,由静态存储芯片(SRAM)组成,容量比较小,Cache存储了频繁访问的内存数据命中率CPU欲访问的信息已在Cache中的比率,设在一段程序执行期间cache完成存取次数为NC,主存完成存取次数为m,h定......
  • 连接池:Memcached的效率倍增器
    连接池:Memcached的效率倍增器在现代应用架构中,Memcached作为一项关键的缓存技术,其性能直接关系到整个系统的响应速度和扩展能力。Memcached的连接池机制是优化资源使用、提升并发访问效率的重要手段。本文将深入探讨Memcached连接池的工作原理,并展示如何通过代码实现和管理......
  • 单点故障克星:Memcached的高可用性策略
    单点故障克星:Memcached的高可用性策略在构建分布式系统时,Memcached作为一个广泛使用的高性能分布式内存缓存系统,其单点故障问题不容忽视。单点故障指的是系统中的一个组件发生故障导致整个系统或服务不可用。本文将深入探讨如何避免Memcached的单点故障问题,确保缓存服务的......