Java Cache模拟器(图形界面)
一、设计思路
(说明,本文来自于《计算机体系结构——量化研究方法》的课程实验)
1.综述
我沿用了助教给的Java模版,完成了基本要求:设置Cache总大小和块大小、设置映射机制、设置替换策略、设置写策略,也完成了所有较高要求:能把Cache分为数据Cache和指令Cache、能设置预取策略、能设置写不命中调块策略并且有UI界面。
在设计上我充分利用了面向对象的设计思路,把各个逻辑部件都封装成内部类,模拟各种行为的方法也封装在内部类的方法中。比较重要的内部类有Instruction类(模拟数据流个条指令)、CacheBlock(模拟各个Cache块特性)、Cache(封装所有Cache块,模拟Cache读写等)。
UI来配置Cache,要点在于将JCombox的选项映射到实际值。我设计的思路沿用模版,加载了din类型流文件后把指令全部读取到内存。这时候我把所有的Cache设置全部保存一遍固定下来并初始化Cache,在运行过程中修改各项JCombox的选项并不会改变当前Cache的设置,仅在下一次加载din流文件时重新载入配置。
下面详细介绍重要方法和各个内部类的设计思想(按照程序逻辑顺序)。
2.内部类CacheBlock
CacheBlock类模拟Cache的块,是一个纯模型类,每一个CacheBlock有一个tag标识(定义同书上Cache地址的块地址的tag)、dirty脏位(表示该块是否被修改过且没有写回Memory)、count计数(用于LRU计算访问次数,这里读写CacheBlock都会增加count)、time(组内Cache块入Cache时间,是一个从0-MAX_LONG的long长整型变量)。
CacheBlock需要一个tag初始化,初始化块count为0、time为-1(由Cache为每组设置的计时器在外部分配入组time)且该块是干净的。
3.Cache初始化,从UI选项到配置的映射
Cache初始化在点击加载din流文件的按键时完成(尽管很奇怪,但是我读din文件时就需要初始化每条Instruction,而初始化Instruction类需要知道当前Cache的配置,所以Cache配置需要先完成,将在后面的分条中介绍。我之前设计的是CachePrepare->ReadFile->CacheInit,后来把CachePrepare和CacheInit合并了)。
初始化Cache之前先置零所有统计量,根据mcacheType决定的类型算出Cache总大小(多少B)以及块大小(多少B)。然后以Cache总大小和块大小来实例化Cache类,接下来的初始化工作在下一小节介绍。
4.内部类Cache
Cache这种以组index作索引,组内块数固定的编址模式,很容易让我们想到用二维数组来模拟。因此最重要的字段是private CacheBlock cache[][],第一维坐标表示组,第二维坐标表示组内块编号。
除此之外,Cache类有一系列记录其属性的字段:cacheSize总大小、blockSize块大小、blockNum总block个数、blockOffset块内地址所占32位2进制地址位数、blockNumInAGroup一个组内块数量、groupNum组总数以及groupOffset组index寻址所占
32位2进制地址位数。这些属性计算公式如下:
blockNum = cacheSize / blockSize;
blockOffset = log2(blockSize);
blockNumInAGroup = pow(2, mwayIndex);
groupNum = blockNum / blockNumInAGroup;
groupOffset = log2(groupNum);
有了这些属性(groupOffset和blockOffset)即可把32位地址分割为三项:tag+index+inblockAddr,后面Instruction类介绍时会再次提到。
最后Cache还为每个组设置了一个入组时间计数器groupFIFOTime[],目的是为了分配实现先进先出队列时选择最早入队的块的time属性。每加载一个新的块入组,groupFIFOTime[index]加一。
Cache类有一系列模拟Cache行为的方法,如read读取Cache、write写入Cache、prefetch预取块、replaceCacheBlock替换块、loadToCache把块从内存加载到Cache等。下面分别介绍这几种方法具体实现:
(1)read
public boolean read(int tag, int index, int inblockAddr) { for (int i = 0; i < blockNumInAGroup; i++) { if (cache[index][i].tag == tag) {//hit cache[index][i].count++; /* Now pretend to send data to CPU */ return true; } } return false; }
读方法传入一个地址的tag、index和inblockAddr,其中inblockAddr并不会用到,这里是为了模拟真实取块内某字节数据所需要提供的inblockAddr。
有了index就直接在该index索引的组内枚举blockNumInAGroup个块,对比每个块的tag。注意:这次实验没有说性能上的优化,那么我这里用O(n)的遍历,如果我们考虑到快速定位tag块可以用HashMap实现(但硬件上应该不会设计这么复杂的数据结构)。
read方法找到该块则返回true,没找到则返回false。其他Cache行为逻辑在单步执行那个方法中完成。
(2)write
public boolean write(int tag, int index, int inblockAddr) { for (int i = 0; i < blockNumInAGroup; i++) { if (cache[index][i].tag == tag) {//hit cache[index][i].count++; cache[index][i].dirty = true; /* Now pretend to write data to Cache */ if (mwriteIndex == 0) {//write back //doing nothing } else if (mwriteIndex == 1) {//write through memoryWriteTime++; /* pretending to write dirty cache to memory after write to cache */ cache[index][i].dirty = false; } return true; } } return false; }
write时在Cache中找目标块也是遍历比较tag,若找到目标则改块访问次数++,dirty置位。模拟写入后(所有模拟动作部分我都用注释标注),判断Cache写回方法,若写回法则此时什么也不做(等待该脏块被替换出去时才写回Memory);若是写之直达法则模拟写Memory并擦除脏位。和read一样找到目标块返回true,否则返回false,其他逻辑交给外部处理。
(3)prefetch
public void prefetch(int nextBlockAddr) { int nextTag = nextBlockAddr / pow(2, groupOffset + blockOffset); int nextIndex = nextBlockAddr / pow(2, blockOffset) % pow(2, groupOffset); replaceCacheBlock(nextTag, nextIndex); }
预取我只做了指令预取(外部逻辑调用),即某条指令所在块Miss时预取下一个指令块。牢记Cache地址=tag: index: inblockAddr,用两个offset位数配合整除和取余运算获取这两部分的tag和index然后调用replaceCacheBlock选择合适的替换算法选择替换块并加载该下一个指令块。
(4)replaceCacheBlock
public void replaceCacheBlock(int tag, int index) { if (mreplaceIndex == 0) {//LRU int lruBlock = 0; for (int i = 1; i < blockNumInAGroup; i++) { if (cache[index][lruBlock].count > cache[index][i].count) { lruBlock = i; } } loadToCache(tag, index, lruBlock); } else if (mreplaceIndex == 1) {//FIFO int fifoBlock = 0; for (int i = 1; i < blockNumInAGroup; i++) { if (cache[index][fifoBlock].time > cache[index][i].time) { fifoBlock = i; } } loadToCache(tag, index, fifoBlock); } else if (mreplaceIndex == 2) {//random int ranBlock = random(0, blockNumInAGroup); loadToCache(tag, index, ranBlock); } }
根据不同的替换策略选取不同的被替换块,LRU则遍历组内最近最少被使用的块用loadToCache方法替换出去(这里使用优先队列是不是性能更好?),FIFO则根据记录组内入Cache时间的time属性选取最先入组的块替换出去,随机则比较简单直接产生一个组内编号的随机数替换。
(5)loadToCache
private void loadToCache(int tag, int index, int groupAddr) { if (mwriteIndex == 0 && cache[index][groupAddr].dirty) { //write back before being replaced; memoryWriteTime++; } cache[index][groupAddr].tag = tag; cache[index][groupAddr].count = 1; cache[index][groupAddr].dirty = false; cache[index][groupAddr].time = groupFIFOTime[index]; groupFIFOTime[index]++; }
替换一个块,首先需要判断是不是用写回法,若是则一定在该Cache块替换出去之前写回内存,然后再设置块的tag位、count置1访问一次、非脏,且用groupFIFOTime分配入组时间。
5.内部类Instruction
Instruction类实例化需要传入din数据流文件中一条数据流指令的opt操作码和地址addr。构造函数内部实现了一个数据选择器和译码器,首先用HexAddr2BinAddr()方法将10进制地址转换为32位2进制地址(32位01字符串)。然后根据Cache设置中计算出的groupOffset和blockOffset分割该字符串,得到tag、index、inblockAddr和blockAddr(tag:index)。注意,这里分割使用的Offset需要考虑Cache类型,统一Cache还是独立Cache/数据Cache还是指令Cache。
HexAddr2BinAddr()就是无脑的switch case转换即可,之前注意补0补齐32位地址即可。
6.内部类DinFileFilter
DinFileFilter是个辅助类,辅助JFileChooser选出我们要的din类型的文件,继承FileFilter类重写accept方法即可。
7.执行(单步、步进到底)和统计量
单步执行需要定义一个ip指针,指向当前执行数据流指令的index。ip等于0时清空一次Cache、重置所有统计量并把UI显示统计量的部分清空刷新。instructions数组中存储在读取din文件就实例化的Instruction实例,在单步执行时我们可以直接利用分割好的地址。取得当前指令后要做的首要事情就是判断Cache类型,统一Cache的指令和数据是存在一个Cache中,分离Cache中他们是分开存储的。下面以分离Cache为例简要分析:
(1)读数据
if (opt == 0) {// read data isHit = dCache.read(tag, index, inblockAddr); if (isHit) { readDataHitTime++; } else { readDataMissTime++; /* Now pretend to find the block in memory */ dCache.replaceCacheBlock(tag, index); /* Now pretend to load the data in block into CPU */ } }
读数据访问dCache,read方法返回是否命中,更新响应统计量计数。若不命中则模拟从Memory调块 -> 调用replaceCacheBlock方法替换旧块并装入新块 -> 最后模拟把这个block中的目标数据返回给CPU。
(2)写数据
<span style="white-space:pre"> </span>else if (opt == 1) {// write data isHit = dCache.write(tag, index, inblockAddr); if (isHit) { writeDataHitTime++; } else { writeDataMissTime++; /* Now pretend to find the block in memory */ if (mallocIndex == 0) {//write alloc /* load the target block into Cache */ dCache.replaceCacheBlock(tag, index); /* pretend to write into the loaded Cache block */ dCache.write(tag, index, inblockAddr); } else if (mallocIndex == 1) {//no write alloc /* do not load the written-missed block into Cache just pretend to write to memory */ memoryWriteTime++; } }
写数据同样是针对dCache,write返回命中与否并据此更新统计量。若写不命中,模拟在Memory中找到该块,根据写不命中策略决定是否把不命中块加载到Cache中:若使用按写分配策略则调该Miss块入Cache,同样用replaceCacheBlock方法并模拟write(注意这里要再调一次write,且不记录模拟统计值);若不按写分配则直接模拟写修改MEM。
(3)读指令
} else if (opt == 2) {// read instruction isHit = iCache.read(tag, index, inblockAddr); if (isHit) { readInstHitTime++; } else { readInstMissTime++; /* Now pretend to find the block in memory */ iCache.replaceCacheBlock(tag, index); /* Now pretend to load the data in block into CPU */ if (mprefetchIndex == 0) {// do not prefetch //doing nothing } else if (mprefetchIndex == 1){// prefetch if instruction missed! iCache.prefetch(instructions[ip].blockAddr + 1); } } }
读指令调用iCache指令Cache的read方法,其他跟dCache读数据一样,除了需要判断一下用不用预取,若需要则传入下一个指令块地址预取该块。
二、分析结论
略,以后补充
三、说明
实验中我和助教给的c++程序比较了几组Cache设置,跑出来结果是一样的。但有的时候偶尔统计不一样,即使所有设置都一样。排除随机因素如随机替换,我给出一点可能不太正确的解释:比如LRU如果组内有某多个块的count值相同,那么我写的程序和助教给的程序选择的替换块可能不同,因此最后结果可能有细微差别(差别不大,1-2条Miss或Hit统计差别)。
我暂时认为我的程序没有问题,但也不能保证...若有Bug请提出,谢谢啦!
四、完整代码
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.File; import javax.swing.*; import javax.swing.border.EtchedBorder; import java.lang.*; import java.util.*; public class MyCacheSim extends JFrame implements ActionListener { private static final long serialVersionUID = 1L; /* ui property */ private JPanel panelTop, panelLeft, panelRight, panelBottom; private JButton execStepBtn, execAllBtn, fileBotton; private JComboBox<String> csBox, bsBox, wayBox, replaceBox, prefetchBox, writeBox, allocBox; private JComboBox<String> icsBox, dcsBox; private JFileChooser fileChooser; private JLabel labelTop,labelLeft,rightLabel,bottomLabel,fileLabel,fileAddrBtn, stepLabel1, stepLabel2, csLabel, bsLabel, wayLabel, replaceLabel, prefetchLabel, writeLabel, allocLabel; private JLabel icsLabel, dcsLabel; private JLabel resultTagLabel[][]; private JLabel resultDataLabel[][]; private JLabel accessTypeTagLabel, addressTagLabel, blockNumberTagLabel, tagTagLabel, indexTagLabel, inblockAddressTagLabel, hitTagLabel; private JLabel accessTypeDataLabel, addressDataLabel, blockNumberDataLabel, tagDataLabel, indexDataLabel, inblockAddressDataLabel, hitDataLabel; private JRadioButton unifiedCacheButton, separateCacheButton; /* options section */ private final String cachesize[] = { "2KB", "8KB", "32KB", "128KB", "512KB", "2MB" }; private final String scachesize[] = { "1KB", "4KB", "16KB", "64KB", "256KB", "1MB" }; private final String blocksize[] = { "16B", "32B", "64B", "128B", "256B" }; private final String way[] = { "直接映象", "2路", "4路", "8路", "16路", "32路" }; private final String replace[] = { "LRU", "FIFO", "RAND" }; private final String pref[] = { "不预取", "不命中预取" }; private final String write[] = { "写回法", "写直达法" }; private final String alloc[] = { "按写分配", "不按写分配" }; //private final String typename[] = { "读数据", "写数据", "读指令" }; //private String hitname[] = {"不命中", "命中" }; private final String resultTags[][] = { {"访问总次数:", "不命中次数:", "不命中率:"}, {"读指令次数:", "不命中次数:", "不命中率:"}, {"读数据次数:", "不命中次数:", "不命中率:"}, {"写数据次数:", "不命中次数:", "不命中率:"} }; /* loading file */ private File file; /* user options record */ private int csIndex, bsIndex, wayIndex, replaceIndex, prefetchIndex, writeIndex, allocIndex; private int mcsIndex, mbsIndex, mwayIndex, mreplaceIndex, mprefetchIndex, mwriteIndex, mallocIndex; private int icsIndex, dcsIndex, micsIndex, mdcsIndex; private int cacheType = 0, mcacheType = 0; /* instruction class */ private class Instruction { int opt; int tag; int index; int blockAddr; int inblockAddr; String addr; public Instruction(int opt, String addr) { this.opt = opt; this.addr = addr; String baddr = this.HexAddr2BinAddr(); //System.out.println(baddr); if (mcacheType == 0 && uCache != null) { this.tag = Integer.parseInt(baddr.substring(0, 32 - uCache.blockOffset - uCache.groupOffset), 2); this.index = Integer.parseInt(baddr.substring(32 - uCache.blockOffset - uCache.groupOffset, 32 - uCache.blockOffset), 2); this.blockAddr = Integer.parseInt(baddr.substring(0, 32 - uCache.blockOffset), 2); this.inblockAddr = Integer.parseInt(baddr.substring(32 - uCache.blockOffset), 2); } if (mcacheType == 1 && iCache != null && dCache != null) { if (opt == 0 || opt == 1) { this.tag = Integer.parseInt(baddr.substring(0, 32 - dCache.blockOffset - dCache.groupOffset), 2); this.index = Integer.parseInt(baddr.substring(32 - dCache.blockOffset - dCache.groupOffset, 32 - dCache.blockOffset), 2); this.blockAddr = Integer.parseInt(baddr.substring(0, 32 - dCache.blockOffset), 2); this.inblockAddr = Integer.parseInt(baddr.substring(32 - dCache.blockOffset), 2); } else if (opt == 2) { this.tag = Integer.parseInt(baddr.substring(0, 32 - iCache.blockOffset - iCache.groupOffset), 2); this.index = Integer.parseInt(baddr.substring(32 - iCache.blockOffset - iCache.groupOffset, 32 - iCache.blockOffset), 2); this.blockAddr = Integer.parseInt(baddr.substring(0, 32 - iCache.blockOffset), 2); this.inblockAddr = Integer.parseInt(baddr.substring(32 - iCache.blockOffset), 2); } } } public String description() { return "opt = " + opt + ", tag = " + tag + ", index = " + index + ", inblockAddr = " + inblockAddr; } private String HexAddr2BinAddr() { StringBuffer sb = new StringBuffer(); int zero = 8 - this.addr.length(); for (int i = 0; i < zero; i++) { sb.append("0000"); } for (int i = 0; i < this.addr.length(); i++) { switch(this.addr.charAt(i)) { case '0': sb.append("0000"); break; case '1': sb.append("0001"); break; case '2': sb.append("0010"); break; case '3': sb.append("0011"); break; case '4': sb.append("0100"); break; case '5': sb.append("0101"); break; case '6': sb.append("0110"); break; case '7': sb.append("0111"); break; case '8': sb.append("1000"); break; case '9': sb.append("1001"); break; case 'a': sb.append("1010"); break; case 'b': sb.append("1011"); break; case 'c': sb.append("1100"); break; case 'd': sb.append("1101"); break; case 'e': sb.append("1110"); break; case 'f': sb.append("1111"); break; default: System.out.println("Data Error!"); } } return sb.toString(); } } /* instruction property */ private Instruction instructions[]; private final int INSTRUCTION_MAX_SIZE = 100000; private int isize; private int ip; private class CacheBlock { int tag; boolean dirty; int count; long time; public CacheBlock(int tag) { this.tag = tag; dirty = false; count = 0; time = -1L; } } /* cache class */ private class Cache { /* cache property */ private CacheBlock cache[][]; private int cacheSize; private int blockSize; private int blockNum; private int blockOffset; private int blockNumInAGroup; private int groupNum; private int groupOffset; private long groupFIFOTime[]; public Cache(int csize, int bsize) { cacheSize = csize; blockSize = bsize; blockNum = cacheSize / blockSize; blockOffset = log2(blockSize); blockNumInAGroup = pow(2, mwayIndex); groupNum = blockNum / blockNumInAGroup; groupOffset = log2(groupNum); cache = new CacheBlock[groupNum][blockNumInAGroup]; for (int i = 0; i < groupNum; i++) { for (int j = 0; j < blockNumInAGroup; j++) { cache[i][j] = new CacheBlock(-1); } } groupFIFOTime = new long[groupNum]; } public boolean read(int tag, int index, int inblockAddr) { for (int i = 0; i < blockNumInAGroup; i++) { if (cache[index][i].tag == tag) {//hit cache[index][i].count++; /* Now pretend to send data to CPU */ return true; } } return false; } public boolean write(int tag, int index, int inblockAddr) { for (int i = 0; i < blockNumInAGroup; i++) { if (cache[index][i].tag == tag) {//hit cache[index][i].count++; cache[index][i].dirty = true; /* Now pretend to write data to Cache */ if (mwriteIndex == 0) {//write back //doing nothing } else if (mwriteIndex == 1) {//write through memoryWriteTime++; /* pretending to write dirty cache to memory after write to cache */ cache[index][i].dirty = false; } return true; } } return false; } public void prefetch(int nextBlockAddr) { int nextTag = nextBlockAddr / pow(2, groupOffset + blockOffset); int nextIndex = nextBlockAddr / pow(2, blockOffset) % pow(2, groupOffset); replaceCacheBlock(nextTag, nextIndex); } public void replaceCacheBlock(int tag, int index) { if (mreplaceIndex == 0) {//LRU int lruBlock = 0; for (int i = 1; i < blockNumInAGroup; i++) { if (cache[index][lruBlock].count > cache[index][i].count) { lruBlock = i; } } loadToCache(tag, index, lruBlock); } else if (mreplaceIndex == 1) {//FIFO int fifoBlock = 0; for (int i = 1; i < blockNumInAGroup; i++) { if (cache[index][fifoBlock].time > cache[index][i].time) { fifoBlock = i; } } loadToCache(tag, index, fifoBlock); } else if (mreplaceIndex == 2) {//random int ranBlock = random(0, blockNumInAGroup); loadToCache(tag, index, ranBlock); } } private void loadToCache(int tag, int index, int groupAddr) { if (mwriteIndex == 0 && cache[index][groupAddr].dirty) { //write back before being replaced; memoryWriteTime++; } cache[index][groupAddr].tag = tag; cache[index][groupAddr].count = 1; cache[index][groupAddr].dirty = false; cache[index][groupAddr].time = groupFIFOTime[index]; groupFIFOTime[index]++; } public void description() { System.out.println("cacheSize = " + cacheSize); System.out.println("blockSize = " + blockSize); System.out.println("blockNum = " + blockNum); System.out.println("blockOffset = " + blockOffset); System.out.println("blockNumInAGroup = " + blockNumInAGroup); System.out.println("groupNum = " + groupNum); System.out.println("groupOffset = " + groupOffset); } } Cache uCache, iCache, dCache; /* * statistic property */ private int readDataMissTime, readInstMissTime, readInstHitTime, readDataHitTime; private int writeDataHitTime, writeDataMissTime; private int memoryWriteTime; /* * JFileChooser Filter Class */ private class DinFileFilter extends javax.swing.filechooser.FileFilter{ public boolean accept(File f) { if (f.isDirectory()) return true; String name = f.getName(); return name.endsWith(".din") || name.endsWith(".DIN"); } public String getDescription() { return ".din"; } } /* * cache simulator class */ public MyCacheSim(){ super("Cache Simulator"); fileChooser = new JFileChooser(); fileChooser.setFileFilter(new DinFileFilter()); draw(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == execAllBtn) { simExecAll(); } if (e.getSource() == execStepBtn) { simExecStep(true); } if (e.getSource() == fileBotton){ int fileOver = fileChooser.showOpenDialog(null); if (fileOver == 0) { String path = fileChooser.getSelectedFile().getAbsolutePath(); fileAddrBtn.setText(path); file = new File(path); /* fix the setting */ mcacheType = cacheType; mcsIndex = csIndex; micsIndex = icsIndex; mdcsIndex = dcsIndex; mbsIndex = bsIndex; mwayIndex = wayIndex; mreplaceIndex = replaceIndex; mprefetchIndex = prefetchIndex; mwriteIndex = writeIndex; mallocIndex = allocIndex; initCache(); readFile(); reloadUI(); } } } /* * 初始化 Cache 模拟器 */ private void initCache() { /* reset statistic properties */ readDataMissTime = 0; readInstMissTime = 0; readDataHitTime = 0; readInstHitTime = 0; writeDataHitTime = 0; writeDataMissTime = 0; memoryWriteTime = 0; /* Cache initialization */ if (mcacheType == 0) { uCache = new Cache(2 * 1024 * pow(4, mcsIndex), 16 * pow(2, mbsIndex)); iCache = null; dCache = null; System.out.println("Unified Cache:"); uCache.description(); } else if (mcacheType == 1) { uCache = null; iCache = new Cache(1 * 1024 * pow(4, micsIndex), 16 * pow(2, mbsIndex)); dCache = new Cache(1 * 1024 * pow(4, mdcsIndex), 16 * pow(2, mbsIndex)); System.out.println("Instruction Cache:"); iCache.description(); System.out.println("Data Cache:"); dCache.description(); } } /* * 将指令和数据流从文件中读入 */ private void readFile() { try { Scanner s = new Scanner(file); instructions = new Instruction[INSTRUCTION_MAX_SIZE]; isize = 0; ip = 0; while(s.hasNextLine()) { String line = s.nextLine(); String[] items = line.split(" "); instructions[isize] = new Instruction(Integer.parseInt(items[0].trim()), items[1].trim()); isize++; } } catch(Exception e) { e.printStackTrace(); } } private void reloadUI() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 2; j++) { resultDataLabel[i][j].setText("0"); } resultDataLabel[i][2].setText("0.00%"); } accessTypeDataLabel.setText("--"); addressDataLabel.setText("--"); blockNumberDataLabel.setText("--"); tagDataLabel.setText("--"); indexDataLabel.setText("--"); inblockAddressDataLabel.setText("--"); hitDataLabel.setText("--"); } /* * 模拟单步执行 */ private void simExecStep(boolean oneStepExec) { ip %= isize; if (ip == 0) { initCache(); reloadUI(); } int opt = instructions[ip].opt; int index = instructions[ip].index; int tag = instructions[ip].tag; int inblockAddr = instructions[ip].inblockAddr; //System.out.printf("opt = %d, tag = %d, index = %d, inblockAddr = %d\n", opt, tag, index, inblockAddr); System.out.println(instructions[ip].description()); boolean isHit = false; if (mcacheType == 0) { /* unified cache */ if (opt == 0) {// read data isHit = uCache.read(tag, index, inblockAddr); if (isHit) { readDataHitTime++; } else { readDataMissTime++; /* Now pretend to find the block in memory */ uCache.replaceCacheBlock(tag, index); /* Now pretend to load the data in block into CPU */ } } else if (opt == 1) {// write data isHit = uCache.write(tag, index, inblockAddr); if (isHit) { writeDataHitTime++; } else { writeDataMissTime++; /* Now pretend to find the block in memory */ if (mallocIndex == 0) {//write alloc /* load the target block into Cache */ uCache.replaceCacheBlock(tag, index); /* pretend to write into the loaded Cache block */ uCache.write(tag, index, inblockAddr); } else if (mallocIndex == 1) {//no write alloc /* do not load the written-missed block into Cache just pretend to write to memory */ memoryWriteTime++; } } } else if (opt == 2) {// read instruction isHit = uCache.read(tag, index, inblockAddr); if (isHit) { readInstHitTime++; } else { readInstMissTime++; /* Now pretend to find the block in memory */ uCache.replaceCacheBlock(tag, index); /* Now pretend to load the data in block into CPU */ if (mprefetchIndex == 0) {// do not prefetch //doing nothing } else if (mprefetchIndex == 1){// prefetch if instruction missed! uCache.prefetch(instructions[ip].blockAddr + 1); } } } } else if (mcacheType == 1) { /* seperated cache */ if (opt == 0) {// read data isHit = dCache.read(tag, index, inblockAddr); if (isHit) { readDataHitTime++; } else { readDataMissTime++; /* Now pretend to find the block in memory */ dCache.replaceCacheBlock(tag, index); /* Now pretend to load the data in block into CPU */ } } else if (opt == 1) {// write data isHit = dCache.write(tag, index, inblockAddr); if (isHit) { writeDataHitTime++; } else { writeDataMissTime++; /* Now pretend to find the block in memory */ if (mallocIndex == 0) {//write alloc /* load the target block into Cache */ dCache.replaceCacheBlock(tag, index); /* pretend to write into the loaded Cache block */ dCache.write(tag, index, inblockAddr); } else if (mallocIndex == 1) {//no write alloc /* do not load the written-missed block into Cache just pretend to write to memory */ memoryWriteTime++; } } } else if (opt == 2) {// read instruction isHit = iCache.read(tag, index, inblockAddr); if (isHit) { readInstHitTime++; } else { readInstMissTime++; /* Now pretend to find the block in memory */ iCache.replaceCacheBlock(tag, index); /* Now pretend to load the data in block into CPU */ if (mprefetchIndex == 0) {// do not prefetch //doing nothing } else if (mprefetchIndex == 1){// prefetch if instruction missed! iCache.prefetch(instructions[ip].blockAddr + 1); } } } } if (oneStepExec || ip == isize - 1) { statisticUIUpdate(instructions[ip], isHit); } ip++; } private void statisticUIUpdate(Instruction inst, boolean isHit) { int totalMissTime = readInstMissTime + readDataMissTime + writeDataMissTime; int totalVisitTime = totalMissTime + readInstHitTime + readDataHitTime + writeDataHitTime; resultDataLabel[0][0].setText(totalVisitTime + ""); resultDataLabel[0][1].setText(totalMissTime + ""); if (totalVisitTime > 0) { double missRate = ((double)totalMissTime / (double)totalVisitTime) * 100; resultDataLabel[0][2].setText(String.format("%.2f", missRate) + "%"); } resultDataLabel[1][0].setText((readInstHitTime + readInstMissTime) + ""); resultDataLabel[1][1].setText(readInstMissTime + ""); if (readInstMissTime + readInstHitTime > 0) { double missRate = ((double)readInstMissTime/(double)(readInstMissTime + readInstHitTime)) * 100; resultDataLabel[1][2].setText(String.format("%.2f", missRate) + "%"); } resultDataLabel[2][0].setText((readDataHitTime + readDataMissTime) + ""); resultDataLabel[2][1].setText(readDataMissTime + ""); if (readDataMissTime + readDataHitTime > 0) { double missRate = ((double)readDataMissTime/(double)(readDataMissTime + readDataHitTime)) * 100; resultDataLabel[2][2].setText(String.format("%.2f", missRate) + "%"); } resultDataLabel[3][0].setText((writeDataHitTime + writeDataMissTime) + ""); resultDataLabel[3][1].setText(writeDataMissTime + ""); if (writeDataMissTime + writeDataHitTime > 0) { double missRate = ((double)writeDataMissTime/(double)(writeDataMissTime + writeDataHitTime)) * 100; resultDataLabel[3][2].setText(String.format("%.2f", missRate) + "%"); } if (inst.opt == 0) { accessTypeDataLabel.setText("读指令"); } else if (inst.opt == 1) { accessTypeDataLabel.setText("读数据"); } else if (inst.opt == 2) { accessTypeDataLabel.setText("写数据"); } else { accessTypeDataLabel.setText("非法指令"); } addressDataLabel.setText(inst.addr); blockNumberDataLabel.setText(inst.blockAddr + ""); tagDataLabel.setText(inst.tag + ""); indexDataLabel.setText(inst.index + ""); inblockAddressDataLabel.setText(inst.inblockAddr + ""); if (isHit) { hitDataLabel.setText("命中"); } else { hitDataLabel.setText("未命中"); } } /* * 模拟执行到底 */ private void simExecAll() { while (ip < isize) { simExecStep(false); } } /* 辅助函数 */ private int pow(int x, int p) { return (int)Math.pow(x, p); } private int log2(int x) { return (int)(Math.log(x) / Math.log(2)); } private int random(int x, int y) { return (int)Math.random() * (y - x) + x; } /* 绘制界面 */ private void unifiedCacheEnabled(boolean enabled) { unifiedCacheButton.setSelected(enabled); csLabel.setEnabled(enabled); csBox.setEnabled(enabled); } private void separateCacheEnabled(boolean enabled) { separateCacheButton.setSelected(enabled); icsLabel.setEnabled(enabled); dcsLabel.setEnabled(enabled); icsBox.setEnabled(enabled); dcsBox.setEnabled(enabled); } private void draw() { setLayout(new BorderLayout(5,5)); panelTop = new JPanel(); panelLeft = new JPanel(); panelRight = new JPanel(); panelBottom = new JPanel(); panelTop.setPreferredSize(new Dimension(800, 50)); panelLeft.setPreferredSize(new Dimension(300, 450)); panelRight.setPreferredSize(new Dimension(500, 450)); panelBottom.setPreferredSize(new Dimension(800, 100)); panelTop.setBorder(new EtchedBorder(EtchedBorder.RAISED)); panelLeft.setBorder(new EtchedBorder(EtchedBorder.RAISED)); panelRight.setBorder(new EtchedBorder(EtchedBorder.RAISED)); panelBottom.setBorder(new EtchedBorder(EtchedBorder.RAISED)); labelTop = new JLabel("Cache Simulator by JCGuo"); labelTop.setAlignmentX(CENTER_ALIGNMENT); JLabel promptLabel = new JLabel(" (Notice! 如果修改了左侧Cache设置请重新加载流文件再运行!)"); promptLabel.setForeground(Color.red); panelTop.add(labelTop); panelTop.add(promptLabel); labelLeft = new JLabel("Cache 参数设置"); labelLeft.setPreferredSize(new Dimension(300, 40)); csLabel = new JLabel("总大小"); csLabel.setPreferredSize(new Dimension(80, 30)); csBox = new JComboBox<String>(cachesize); csBox.setPreferredSize(new Dimension(90, 30)); csBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { csIndex = csBox.getSelectedIndex(); } }); //cache 种类 unifiedCacheButton = new JRadioButton("统一Cache:", true); unifiedCacheButton.setPreferredSize(new Dimension(100, 30)); unifiedCacheButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { separateCacheEnabled(false); unifiedCacheEnabled(true); cacheType = 0; } }); separateCacheButton = new JRadioButton("分离Cache:"); separateCacheButton.setPreferredSize(new Dimension(100, 30)); separateCacheButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { separateCacheEnabled(true); unifiedCacheEnabled(false); cacheType = 1; } }); icsLabel = new JLabel("指令Cache"); icsLabel.setPreferredSize(new Dimension(80, 30)); dcsLabel = new JLabel("数据Cache"); dcsLabel.setPreferredSize(new Dimension(80, 30)); JLabel emptyLabel = new JLabel(""); emptyLabel.setPreferredSize(new Dimension(100, 30)); icsBox = new JComboBox<String>(scachesize); icsBox.setPreferredSize(new Dimension(90, 30)); icsBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { icsIndex = icsBox.getSelectedIndex(); } }); dcsBox = new JComboBox<String>(scachesize); dcsBox.setPreferredSize(new Dimension(90, 30)); dcsBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { dcsIndex = dcsBox.getSelectedIndex(); } }); separateCacheEnabled(false); unifiedCacheEnabled(true); //cache 块大小设置 bsLabel = new JLabel("块大小"); bsLabel.setPreferredSize(new Dimension(120, 30)); bsBox = new JComboBox<String>(blocksize); bsBox.setPreferredSize(new Dimension(160, 30)); bsBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { bsIndex = bsBox.getSelectedIndex(); } }); //相连度设置 wayLabel = new JLabel("相联度"); wayLabel.setPreferredSize(new Dimension(120, 30)); wayBox = new JComboBox<String>(way); wayBox.setPreferredSize(new Dimension(160, 30)); wayBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { wayIndex = wayBox.getSelectedIndex(); } }); //替换策略设置 replaceLabel = new JLabel("替换策略"); replaceLabel.setPreferredSize(new Dimension(120, 30)); replaceBox = new JComboBox<String>(replace); replaceBox.setPreferredSize(new Dimension(160, 30)); replaceBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { replaceIndex = replaceBox.getSelectedIndex(); } }); //欲取策略设置 prefetchLabel = new JLabel("预取策略"); prefetchLabel.setPreferredSize(new Dimension(120, 30)); prefetchBox = new JComboBox<String>(pref); prefetchBox.setPreferredSize(new Dimension(160, 30)); prefetchBox.addItemListener(new ItemListener(){ public void itemStateChanged(ItemEvent e){ prefetchIndex = prefetchBox.getSelectedIndex(); } }); //写策略设置 writeLabel = new JLabel("写策略"); writeLabel.setPreferredSize(new Dimension(120, 30)); writeBox = new JComboBox<String>(write); writeBox.setPreferredSize(new Dimension(160, 30)); writeBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { writeIndex = writeBox.getSelectedIndex(); } }); //调块策略 allocLabel = new JLabel("写不命中调块策略"); allocLabel.setPreferredSize(new Dimension(120, 30)); allocBox = new JComboBox<String>(alloc); allocBox.setPreferredSize(new Dimension(160, 30)); allocBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { allocIndex = allocBox.getSelectedIndex(); } }); //选择指令流文件 fileLabel = new JLabel("选择指令流文件"); fileLabel.setPreferredSize(new Dimension(120, 30)); fileAddrBtn = new JLabel(); fileAddrBtn.setPreferredSize(new Dimension(210,30)); fileAddrBtn.setBorder(new EtchedBorder(EtchedBorder.RAISED)); fileBotton = new JButton("浏览"); fileBotton.setPreferredSize(new Dimension(70,30)); fileBotton.addActionListener(this); panelLeft.add(labelLeft); panelLeft.add(unifiedCacheButton); panelLeft.add(csLabel); panelLeft.add(csBox); panelLeft.add(separateCacheButton); panelLeft.add(icsLabel); panelLeft.add(icsBox); panelLeft.add(emptyLabel); panelLeft.add(dcsLabel); panelLeft.add(dcsBox); panelLeft.add(bsLabel); panelLeft.add(bsBox); panelLeft.add(wayLabel); panelLeft.add(wayBox); panelLeft.add(replaceLabel); panelLeft.add(replaceBox); panelLeft.add(prefetchLabel); panelLeft.add(prefetchBox); panelLeft.add(writeLabel); panelLeft.add(writeBox); panelLeft.add(allocLabel); panelLeft.add(allocBox); panelLeft.add(fileLabel); panelLeft.add(fileAddrBtn); panelLeft.add(fileBotton); //*****************************右侧面板绘制*****************************************// //模拟结果展示区域 rightLabel = new JLabel("模拟结果"); rightLabel.setPreferredSize(new Dimension(500, 40)); panelRight.add(rightLabel); resultTagLabel = new JLabel[4][3]; resultDataLabel = new JLabel[4][3]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { resultTagLabel[i][j] = new JLabel(resultTags[i][j]); resultTagLabel[i][j].setPreferredSize(new Dimension(70, 40)); if (j != 2) { resultDataLabel[i][j] = new JLabel("0"); } else { resultDataLabel[i][j] = new JLabel("0.00%"); } resultDataLabel[i][j].setPreferredSize(new Dimension(83, 40)); panelRight.add(resultTagLabel[i][j]); panelRight.add(resultDataLabel[i][j]); } if (i == 0) { JLabel label = new JLabel("其中:"); label.setPreferredSize(new Dimension(500, 40)); panelRight.add(label); } } /* stepLabel1 = new JLabel(); stepLabel1.setVisible(false); stepLabel1.setPreferredSize(new Dimension(500, 40)); stepLabel2 = new JLabel(); stepLabel2.setVisible(false); stepLabel2.setPreferredSize(new Dimension(500, 40)); panelRight.add(stepLabel1); panelRight.add(stepLabel2); */ accessTypeTagLabel = new JLabel("访问类型:"); addressTagLabel = new JLabel("地址:"); blockNumberTagLabel = new JLabel("块号:"); tagTagLabel = new JLabel("标记Tag:"); indexTagLabel = new JLabel("组索引:"); inblockAddressTagLabel = new JLabel("块内地址:"); hitTagLabel = new JLabel("命中情况:"); accessTypeDataLabel = new JLabel("--"); addressDataLabel = new JLabel("--"); blockNumberDataLabel = new JLabel("--"); tagDataLabel = new JLabel("--"); indexDataLabel = new JLabel("--"); inblockAddressDataLabel = new JLabel("--"); hitDataLabel = new JLabel("--"); accessTypeTagLabel.setPreferredSize(new Dimension(80, 40)); accessTypeDataLabel.setPreferredSize(new Dimension(80, 40)); addressTagLabel.setPreferredSize(new Dimension(80, 40)); addressDataLabel.setPreferredSize(new Dimension(200, 40)); panelRight.add(accessTypeTagLabel); panelRight.add(accessTypeDataLabel); panelRight.add(addressTagLabel); panelRight.add(addressDataLabel); blockNumberTagLabel.setPreferredSize(new Dimension(80, 40)); blockNumberDataLabel.setPreferredSize(new Dimension(200, 40)); hitTagLabel.setPreferredSize(new Dimension(80, 40)); hitDataLabel.setPreferredSize(new Dimension(80, 40)); panelRight.add(blockNumberTagLabel); panelRight.add(blockNumberDataLabel); panelRight.add(hitTagLabel); panelRight.add(hitDataLabel); tagTagLabel.setPreferredSize(new Dimension(60, 40)); tagDataLabel.setPreferredSize(new Dimension(70, 40)); indexTagLabel.setPreferredSize(new Dimension(60, 40)); indexDataLabel.setPreferredSize(new Dimension(70, 40)); inblockAddressTagLabel.setPreferredSize(new Dimension(60, 40)); inblockAddressDataLabel.setPreferredSize(new Dimension(100, 40)); panelRight.add(tagTagLabel); panelRight.add(tagDataLabel); panelRight.add(indexTagLabel); panelRight.add(indexDataLabel); panelRight.add(inblockAddressTagLabel); panelRight.add(inblockAddressDataLabel); //*****************************底部面板绘制*****************************************// bottomLabel = new JLabel("执行控制"); bottomLabel.setPreferredSize(new Dimension(800, 30)); execStepBtn = new JButton("步进"); execStepBtn.setLocation(100, 30); execStepBtn.addActionListener(this); execAllBtn = new JButton("执行到底"); execAllBtn.setLocation(300, 30); execAllBtn.addActionListener(this); panelBottom.add(bottomLabel); panelBottom.add(execStepBtn); panelBottom.add(execAllBtn); add("North", panelTop); add("West", panelLeft); add("Center", panelRight); add("South", panelBottom); setSize(820, 620); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { new MyCacheSim(); } }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。