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();
	}
}



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。