阿里的運維大牛教你:如何編程模擬計算機中的高速緩存

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.編寫的程序要能讀取指定文件內的指令,根據不同的指令完成不同的動作,下面爲指令內容示例。

I0400d7d4,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(前面說了,不需要管塊偏移)。

  關於緩存和內存數據交換的詳細介紹可以看下這個24張圖7000字詳解計算機中的高速緩存。

  下面我們開始編寫代碼。

2.1 讀取文件

  getopt()該函數能夠幫助程序分析C語言命令行程序輸入的參數。

intgetopt(intargc,char*constargv[ ],constchar* optstring);

  重點說下getopt()函數。前兩個形參是main函數傳入的參數,即我們輸入的命令行,第三個形參是 optstring“選項字符串”,即標識哪些字母表示了操作。

  如"a:b:cd::e",字母后帶一個冒號(例中的a、b)表明這個操作帶參數,字母后的內容需要讀取,存放到它內部變量 extern char * optarg中。

  字母不帶冒號(例中的c、e)表明該操作不帶參數,後面輸入的內容仍看作操作符處理。字母后帶兩個冒號(例中的d)表明該操作後參數是可選的,但是要求如果帶參數時參數與操作符不能有空格,如-d123是對的,而-d 123會報錯。當讀取了全部的輸入的命令後 getopt()返回-1。

while((opt = getopt(argc,argv,"s:E:b:t:")) !=-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函數,該函數能夠幫助用戶處理文本文件中輸入的格式化數據。

intfscanf(FILE *stream,char*format[,argument...]);

  stream-這是指向 FILE 對象的指針,該 FILE 對象標識了流。

  format-這是 C 字符串,包含了以下各項中的一個或多個:空格字符、非空格字符和format 說明符。

2.2 高速緩存定義結構體

  實驗要求中說明了,不需要處理b,只需認爲每行中有一個block。因此cache_line結構體中包括有效位,標記位,時間戳三個變量就夠了。stamp記錄的是block 的使用時間,每被使用一次,block++。因此,stamp越大表明該block越是最近被使用。具體代碼如下。

typedefstruct{intvalid_bits;unsignedtag;intstamp;}cache_line;

2.3 初始化Cache

  定義一個Cache[S] [E]大小的二維數組。這樣Cache就模擬好了。

voidinit(){ cache = (cache_line**)malloc(sizeof(cache_line*)*S);//malloc開闢空間for(inti=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。

unsigneds_address =(address>>b) & ((0xffffffff)>>(32-s));//索引位unsignedt_address = address>>(s+b);//標記位

2.5 LRU策略

  替換策略使用的是LRU的緩存替換策略。如果該set存滿了,我每次要找到stamp最小的替換。爲了方便,我把stamp初始化爲0,之後每個操作+1. 當stamp= 0的時候就代表不valid。

voidtime(){for(inti=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 20:40:12

 * @LastEditors: 嵌入式與Linux那些事

 * @LastEditTime: 2021-1-1 22:11:58

 */#include"cachelab.h"#include<getopt.h>#include<stdlib.h>#include<unistd.h>#include<stdio.h>#include<stddef.h>typedefstruct{intvalid_bits;unsignedtag;intstamp;}cache_line;char* filepath =NULL;ints,E,b,S;// s 表示組 ,E表示行,每一行有 2^b位 ,S = 2^s 組inthit=0,miss=0,eviction=0;cache_line** cache =NULL;voidinit(){ cache = (cache_line**)malloc(sizeof(cache_line*)*S);//malloc開闢空間for(inti=0;i>b) & ((0xffffffff)>>(32-s));//從地址分解出索引位unsignedt_address = address>>(s+b);//從地址分解出標記位//判斷tag爲是否相等,是否命中for(inti=0;itag ==t_address){   cache[s_address][i].stamp =0;//被使用了hit++;return;  } }//更新高速緩存cachefor(inti=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; }//更新stampvoidtime(){for(inti=0;i0){switch(operation){case'L':    update(address);break;case'M':    update(address);case'S':    update(address);break;  }  time(); }for(inti=0;i

3. 測試結果

HNU的自動評分系統會測試我們編寫的代碼是否可以通過所有的測試用例,和Reference simulator的對比表明,結果完全相同。測試通過!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章