1. 實驗要求
2. 編程
2.1 讀取文件
2.2 高速緩存定義結構體
2.3 初始化Cache
2.4 解析輸入的指令
2.5 LRU策略
2.6 更新高速緩存Cache
2.7 完整代碼
3. 測試結果
1. 實驗要求
??1.編程模擬Cahce的命中,不命中,替換等行為。
??2.編寫的程序必須對任意s,E和b正確工作。
??3.本實驗不涉及真實的數據讀寫,不需要考慮block的細節,每行只有一個block。
??4.編寫的程序要能讀取指定文件內的指令,根據不同的指令完成不同的動作,下面為指令內容示例。
?
I?0400d7d4,8??????# M?0421c7f0,4??????#?修改access cache,?即讀寫access cache兩次,0421c7f0:修改的地址,8:修改的長度 L?04f6b868,8??????#?讀access cache,04f6b868:讀取的地址,8:讀取長度 S?7ff0005c8,8?????#?寫access cache,0421c7f0:寫的地址,8:寫的長度
?
2. 編程
??考慮模擬一個Cache的行為需要用到哪些變量?
計算機中的高速緩存模型
??Cache有組數S、一組包含的行數E,存儲塊的字節大小B,Cache的容量C=S×E×B。
??地址的構成:標識位t、組索引s、塊偏移b(前面說了,不需要管塊偏移)。
??下面我們開始編寫代碼。
2.1 讀取文件
??getopt()該函數能夠幫助程序分析C語言命令行程序輸入的參數。
?
int?getopt(int?argc,char?*?const?argv[?],const?char?*?optstring);
?
??重點說下getopt()函數。前兩個形參是main函數傳入的參數,即我們輸入的命令行,第三個形參是 optstring“選項字符串”,即標識哪些字母表示了操作。
??如"acd::e",字母后帶一個冒號(例中的a、b)表明這個操作帶參數,字母后的內容需要讀取,存放到它內部變量 extern char * optarg中。
??字母不帶冒號(例中的c、e)表明該操作不帶參數,后面輸入的內容仍看作操作符處理。字母后帶兩個冒號(例中的d)表明該操作后參數是可選的,但是要求如果帶參數時參數與操作符不能有空格,如-d123是對的,而-d 123會報錯。當讀取了全部的輸入的命令后 getopt()返回-1。
?
while((opt?=?getopt(argc,argv,"sb"))?!=-1){???????????//解析命令行參數 ??switch(opt){ ??case?'s': ???s=atoi(optarg); ???break; ??case?'E': ???E=atoi(optarg); ???break; ??case?'b': ???b=atoi(optarg); ???break; ??case?'t': ???filepath?=?optarg; ???break; ??} ?}
?
?? fscanf函數,該函數能夠幫助用戶處理文本文件中輸入的格式化數據。
?
int?fscanf(FILE?*stream,?char?*format[,argument...]);
?
??stream-這是指向 FILE 對象的指針,該 FILE 對象標識了流。
??format-這是 C 字符串,包含了以下各項中的一個或多個:空格字符、非空格字符和format 說明符。
2.2 高速緩存定義結構體
??實驗要求中說明了,不需要處理b,只需認為每行中有一個block。因此cache_line結構體中包括有效位,標記位,時間戳三個變量就夠了。stamp記錄的是block 的使用時間,每被使用一次,block++。因此,stamp越大表明該block越是最近被使用。具體代碼如下。
?
typedef?struct{ ?int?valid_bits; ?unsigned??tag; ?int?stamp; }cache_line;
?
2.3 初始化Cache
??定義一個Cache[S] [E]大小的二維數組。這樣Cache就模擬好了。
?
void?init(){ ?cache?=?(cache_line**)malloc(sizeof(cache_line*)*S);?????????????//malloc開辟空間 ?for(int?i=0;i?
2.4 解析輸入的指令
??先分析每個輸入的指令應該被如何操作。如果是I,則不是數據操作,直接忽略。如果是L或者S,則需要進行一次hit-miss-eviction檢測,如果是M,則相當于先L再S,需要進行兩次hit-miss-eviction檢測。然后考慮hit-miss- eviction檢測細節。
?
?while(fscanf(file,"?%c?%x,%d",&operation,&address,&size)>0){ ??switch(operation){ ???case?'L': ????update(address); ????break; ???case?'M': ????update(address); ???case?'S': ????update(address); ????break; ??} ??time(); ?}?
??首先需要對讀取的地進有分析,低b位表示 block偏移,本實驗中不需要計算block偏移。中間s位是 set index位,表示對那個行操作。其余t位是tag位。用于標明對應的line是否有效。我們需要對得到的地址進行如下操作,解析出t和s。
?
?unsigned?s_address?=(address>>b)?&?((0xffffffff)>>(32-s));????????????????//索引位 ?unsigned?t_address?=?address>>(s+b);?????????????????????????????????????//標記位?
2.5 LRU策略
??替換策略使用的是LRU的緩存替換策略。如果該set存滿了,我每次要找到stamp最小的替換。為了方便,我把stamp初始化為0,之后每個操作+1. 當stamp= 0的時候就代表不valid。
?
void?time(){ ?for(int?i=0;i?max_stamp){ ???max_stamp?=?cache[s_address][i].stamp; ???max_i?=?i; ??} ?}?
2.6 更新高速緩存Cache
??Cache的容量有限,當滿的時候需要替換行,先遍歷當前組,判斷它滿了沒有,如何判斷是否滿,可以遍歷所有的行,只要有一個有效位為0,(有效位的作用是說明該行是否存儲了數據,通俗的理解就是是否為空)則該組未滿。
?
for(int?i=0;i?
2.7 完整代碼
?
/* ?*?@Description:?編程模擬Cache ?*?@Version:?V1.0 ?*?@Autor:?嵌入式與Linux那些事 ?*?@Date:?2021-1-1?2012 ?*?@LastEditors:?嵌入式與Linux那些事 ?*?@LastEditTime:?2021-1-1?2258 ?*/ #include?"cachelab.h" #include?#include? #include? #include? #include? typedef?struct{ ?int?valid_bits; ?unsigned??tag; ?int?stamp; }cache_line; char*?filepath?=?NULL; int?s,E,b,S;??????????????????????????//?s?表示組?,E表示行,每一行有?2^b位?,S?=?2^s?組 int?hit=0,miss=0,eviction=0; cache_line**?cache?=?NULL; void?init(){ ?cache?=?(cache_line**)malloc(sizeof(cache_line*)*S);?????????????//malloc開辟空間 ?for(int?i=0;i >b)?&?((0xffffffff)>>(32-s));???????????//從地址分解出索引位 ?unsigned?t_address?=?address>>(s+b);?????????????????????????????????//從地址分解出標記位 ?//判斷tag為是否相等,是否命中 ????for(int?i=0;itag?==t_address){ ???cache[s_address][i].stamp?=?0;???????//被使用了 ???hit++; ???return; ??} ?} ????//更新高速緩存cache ?for(int?i=0;i ?max_stamp){ ???max_stamp?=?cache[s_address][i].stamp; ???max_i?=?i; ??} ?} ?eviction++; ?miss++; ?cache[s_address][max_i].tag?=?t_address; ?cache[s_address][max_i].stamp?=?0;? } //更新stamp void?time(){ ?for(int?i=0;i 0){ ??switch(operation){ ???case?'L': ????update(address); ????break; ???case?'M': ????update(address); ???case?'S': ????update(address); ????break; ??} ??time(); ?} ?for(int?i=0;i?
3. 測試結果
HNU的自動評分系統會測試我們編寫的代碼是否可以通過所有的測試用例,和Reference simulator的對比表明,結果完全相同。測試通過!
image-20201231160527635
?
?
審核編輯:湯梓紅?
評論
查看更多