Beruflich Dokumente
Kultur Dokumente
SCIENCE
Group: Nagarjuna E
Puneeth SM
Sonal HP
Sangeeta Patil
1 Introduction
Coherence protocols
If the protocol design states that whenever any copy of the shared data
is changed, all the other copies must be "updated" to reflect the change,
then it is a write update protocol. If the design states that on a write to a
cached copy by any processor requires other processors to
discard/invalidate their cached copies, then it is a write invalidate
protocol.
In this simple protocol the memory is always consistent with the most
recently updated cache copy. Multiple processors can read block copies
from main memory safely until one processor updates its copy. At this
time, all cache copies are invalidated and the memory is updated to
remain consistent. The block statesand protocol are summarized in Table
Example: Consider a bus-based shared memory with two processors P
and Q as shown in Figure 4.6. Let us see how the cache coherence is
maintained using Write- Invalidate Write-Through protocol. Assume that
that X in memory was originally set to 5 and the following operations
were performed in the order given:
can safely read these blocks from their caches until one processor updates its copy. At
this time, the writer becomes the only owner of the valid block and all other copies are
invalidated. The block states and protocol are summarized in Table
Example
Consider the shared memory system of Figure 4.6 and the following
operations: (1) P reads X; (2) Q reads X; (3) Q updates X; (4) Q reads X; (5) Q updates X;
(6) P updates X; (7) Q reads X. Table 4.6 shows the contents of memory and the two
caches after the execution of each operation when Write-Invalidate Write-Back was used
for cache coherence. The table also shows the state of the block containing X in Ps
cache and Qs cache.
Implementation :
V valid
D dirty bit, signifies that data in the cache is not the same as in
memory
S shared
Each cache line is in one of the following states: "dirty" (has been
updated by local processor), "valid", "invalid" or "shared". A cache line
contains a value, and it can be read or written. Writing on a cache line
changes the value. Each value is either in main memory (which is very
slow to access), or in one or more local caches (which is fast). When a
block is first loaded into the cache, it is marked as "valid".
On a read miss to the local cache, the read request is broadcast on the
bus. All cache controllers monitor the bus. If one has cached that address
and it is in the state "dirty", it changes the state to "valid" and sends the
copy to requesting node. The "valid" state means that the cache line is
current. On a local write miss (an attempt to write that value is made,
but it's not in the cache), bus snooping ensures that any copies in other
caches are set to "invalid". "Invalid" means that a copy used to exist in
the cache, but it is no longer current.
Tag | ID | V | D | S
---------------------
1111 | 00 | 1 | 0 | 0
0000 | 01 | 0 | 0 | 0
0000 | 10 | 1 | 0 | 1
0000 | 11 | 0 | 0 | 0
After a write of address 1111 00, it would change into this:
Tag | ID | V | D | S
---------------------
1111 | 00 | 1 | 1 | 0
0000 | 01 | 0 | 0 | 0
0000 | 10 | 1 | 0 | 1
0000 | 11 | 0 | 0 | 0
The caching logic monitors the bus and detects if any cached memory is
requested. If the cache is dirty and shared and the bus requests that
memory, the snooping elements will supply the value from the cache
then notify every unit that needs that memory, that the memory was
updated. When the other units are notified of the updated cache, they
will turn off the valid bit for their cache of that variable. Thus the original
cache will be marked as exclusive (S bit would be zero)
When invalidating an address marked as dirty (i.e. one cache would have
a dirty address and the other cache is writing) then the cache will ignore
that request. The new cache will be marked as dirty, valid and exclusive
and that cache will now take responsibility for the address.
MSI Protocol
In computing, the MSI protocol - a basic cache-coherence protocol -
operates in multiprocessor systems. As with other cache coherency
protocols, the letters of the protocol name identify the possible states in
which a cache line can be.
In MSI, each block contained inside a cache can have one of three possible states:
Modified: The block has been modified in the cache. The data in the cache is then
inconsistent with the backing store (e.g. memory). A cache with a block in the "M" state has
the responsibility to write the block to the backing store when it is evicted.
Shared: This block is unmodified and exists in read-only state in at least one cache. The
cache can evict the data without writing it to the backing store.
Invalid: This block is either not present in the current cache or has been invalidated by a
bus request, and must be fetched from memory or another cache if the block is to be stored
in this cache.[1]
Design and Implementation
of MESI Coherency Protocols
using JAVA
APPENDIX
package sim;
import
java.io.BufferedWriter;
import java.io.File;
import
java.io.FileWriter;
import
java.io.IOException;
import
java.text.DateFormat;
import
java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import
java.util.TimerTask;
import sim.controller.CacheController;
import
sim.controller.MESICacheController;
import sim.enumerators.CacheProtocol;
import sim.enumerators.Level;
import
sim.interconnect.SnoopyBus;
import sim.processor.Cache;
import sim.processor.Core;
/**
* Main class of the MESI cache simulator.
*/
public class CacheSimulator {
/**
* list of controllers. Used for printing test statistics
*/
static List<CacheController> cntrlrs = new ArrayList<CacheController>();
/**
* main method of the cache simulator. This method takes the number of cores as command
* line argument and creates that many number of threads. Each thread represents a processor core.
* {@link Core} sends out random read and write requests to its private cache. The cache controller
* can be either {@link MESICacheController} depending on the
* {@link CacheProtocol} selected. The test statistics such as total requests generated, read hits,
* write hits, read miss and write miss are printed and saved to a log file. The time for which
* simulator must run can also be specified in this method.
* @param args - args[0] - the number of cores to be created.
*/
public static void main(String[] args) {
if(args.length !=1){
System.out.println("Invalid number of arguments!");
return;
}
//create a bus object which handles the traffic between caches. Any communication
//in between caches is synchronised so as to avoid data races.
SnoopyBus bus = new SnoopyBus();
//create a number of cores and start sending random read and write requests.
for(int i=0;i<numCores;i++){
//schedule a task to stop execution after specified time and print test statistics.
timer.schedule(new TimerTask() {
@Override
public void run()
{ System.out.println("Exiting...."
);
DateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_hh_mm");
String filename = formatter.format(new Date())+"_"+protocol+"_"+mins+"_"+numCores;
try {
//create a writer for a new log file
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename)));
long readHits = 0;
long writeHits = 0;
long readMiss = 0;
long writeMiss = 0;
long totalRequests = 0;
long l2AccessCount = 0;
(tag);
//return the value stored in the selected cache line at the specified index.
CacheLine cacheLine = cache.getCacheLines().get(tag);
return cacheLine.getValue(index);
}
/**
* The cache controller implementing MESI protocol
*/
public class MESICacheController extends CacheController{
/**
* Constructor.
*/
public MESICacheController(Cache cache,SnoopyBus bus) {
this.cache = cache;
this.bus = bus;
}
/**
* load the cache line from other L1caches or L2Cache.
* Update status to Shared if loaded from another L1,
* exclusive if loaded from L2
* @param tag - the address to be loaded to cache
*/
protected void load(long tag) {
synchronized (bus) {
//broadcast load request to other l1 caches
List<CacheLine> cacheLines = bus.getCacheLineCopiesFromOtherL1Caches(this.cache, tag);
//if there are no copies in other L1 caches, load the cache line from L2 cache.
if (cacheLines.isEmpty()) {
//as the cache line is loaded from L2, it is exclusive to this cache
//and can be marked as Exclusive.
loadedCacheLine.setCacheState(CacheState.EXCLUSIVE);
newCacheLine.setCacheState(CacheState.EXCLUSIVE);
} else {
//take the first copy in the copies returned from other L1 caches.
loadedCacheLine = cacheLines.get(0);
/**
* bus that acts as interconnect between L1 and L2 caches and maintains cache coherency
*
*/
public class SnoopyBus {
/**
* the shared L2 cache
*/
private Cache l2Cache = null;
/**
* the list of private L1 caches present in the system
*/
private List<Cache> l1Caches = new ArrayList<Cache>();
/**
* adds new L1 cache
* @param cache
*/
public void addNewL1Cache(Cache cache) {
l1Caches.add(cache);
}
/**
* gets all L1 caches
* @return list of L1 caches
*/
public List<Cache> getL1Caches() {
return l1Caches;
}
/**
* get the L2 cache
* @return L2 cache
*/
public Cache getL2Cache() {
return l2Cache;
}
/**
* sets the L2 cache connection
*/
public void setL2Cache(Cache l2Cache) {
this.l2Cache = l2Cache;
}
/**
* invalidate all cache lines which are modified
*/
public synchronized void invalidateCacheLine(long tag) {
for(Cache l1Cache: l1Caches){
CacheLine cacheLine = l1Cache.getCacheLines().get(tag);
if(cacheLine != null)
{ cacheLine.setCacheState(CacheState.INVALID);
}
}
}
/**
* mark a cache line as shared in all the caches holding it.
* @param tag - the address tag
*/
public void markCacheLineShared(long tag) {
for(Cache l1Cache: l1Caches){
CacheLine cacheLine = l1Cache.getCacheLines().get(tag);
if(cacheLine != null)
{ cacheLine.setCacheState(CacheState.SHARED);
}
}
}
/**
* get the copies of the same cache line from other processors.
* @param cache - the cache requesting other copies
* @param tag - the tag of cache line
* @return the list of cache line copies.
*/
public List<CacheLine> getCacheLineCopiesFromOtherL1Caches(Cache cache,long tag) {
List<CacheLine> cacheLines = new ArrayList<CacheLine>();
for (Cache l1Cache :l1Caches) {
if(l1Cache == cache)continue;
CacheLine cacheLine = l1Cache.getCacheLine(tag);
if(cacheLine != null){
if(cacheLine.getCacheState() != CacheState.INVALID)
{ cacheLines.add(cacheLine);
}
}
}
return cacheLines;
}
/**
* get a cache line from L2 cache.
* @param tag - the tag of the cache line.
* @return the retrieved cache line
*/
public CacheLine getCacheLineFromL2(long tag) {
CacheLine loadedCacheLine;
//if the core is requesting the cache line for the first time
//create a new cache line in L2, fill it with random integer
//values and return it
if (l2Cache.getCacheLine(tag) == null) {
*
*/
public class Cache {
/**
* a map holding the address tag and cache lines
*/
private Map<Long, CacheLine> cacheLines;
/**
* maximum size of the cache.
*/
private int cacheSize;
/**
* the level of the cache.In this implementation the possible values are L1 and L2.
*/
private Level level;
/**
* constructor.
* @param size - cache size
* @param level- cache level.
*/
public Cache(int size, Level level) {
cacheLines = new HashMap<Long, CacheLine>();
this.cacheSize = size;
this.level = level;
}
/**
* get the cache line for a given tag
* @param tag - the address tag
* @return the cache line
*/
public CacheLine getCacheLine(long tag) {
return cacheLines.get(tag);
}
/**
* get all cache lines in the cache.
* @return cacheLines
*/
public Map<Long, CacheLine> getCacheLines() {
return cacheLines;
}
/**
* get the maximum size of the cache.
* @return cache size
*/
public int getCacheSize() {
return cacheSize;
}
/**
* get the cache level
* @return the cache level.
*/
public Level getLevel() {
return level;
}
}
/**
* The class representing a cache line.
*
*/
public class CacheLine {
/**
* @return the tag
*/
public long getTag() {
return tag;
}
/**
* @param tag the tag to set
*/
public void setTag(long tag)
{ this.tag = tag;
}
/**
* @return the value
*/
public long getValue(int index) { return
values[index];
}
/**
* @param value the value to set
*/
public void setValues(long[] values)
{ this.values = values;
}
/**
* @return the cacheState
*/
public CacheState getCacheState()
{ return cacheState;
}
/**
* @param cacheState the cacheState to set
*/
public void setCacheState(CacheState cacheState)
{ this.cacheState = cacheState;
}
/***
* returns a string representation of the object
*/
@Override
public String toString() {
return tag+" , "+values+" , "+cacheState;
}
/**
* returns all values in the selected cache line as an array.
* @return array of integers
*/
public long[] getValues() {
return values;
}