|
| 1 | +#include <bits/stdc++.h> |
| 2 | +using namespace std; |
| 3 | + |
| 4 | +// Cache Block |
| 5 | +struct CacheBlock |
| 6 | +{ |
| 7 | + unsigned long long tag; |
| 8 | + bool valid; |
| 9 | + CacheBlock() : tag(0), valid(false) {} |
| 10 | +}; |
| 11 | + |
| 12 | +// REPLACEMENT POLICY INTERFACE |
| 13 | +class ReplacementPolicy |
| 14 | +{ |
| 15 | +public: |
| 16 | + virtual int chooseVictim(int setIndex, const vector<unsigned long long> &future = {}) = 0; |
| 17 | + virtual void onAccess(int setIndex, int lineIndex) = 0; |
| 18 | + virtual void onInsert(int setIndex, int lineIndex) = 0; |
| 19 | + virtual void reset() = 0; |
| 20 | + virtual ~ReplacementPolicy() {} |
| 21 | +}; |
| 22 | + |
| 23 | +// FIRST IN FIRST OUT (FIFO) ALGORITHM |
| 24 | +class FIFOReplacement : public ReplacementPolicy |
| 25 | +{ |
| 26 | + vector<vector<int>> order; |
| 27 | + |
| 28 | +public: |
| 29 | + FIFOReplacement(int sets, int ways) { order.assign(sets, {}); } |
| 30 | + int chooseVictim(int setIndex, const vector<unsigned long long> & = {}) |
| 31 | + { |
| 32 | + int victim = order[setIndex].front(); |
| 33 | + order[setIndex].erase(order[setIndex].begin()); |
| 34 | + return victim; |
| 35 | + } |
| 36 | + void onAccess(int, int) {} |
| 37 | + void onInsert(int setIndex, int lineIndex) { order[setIndex].push_back(lineIndex); } |
| 38 | + void reset() |
| 39 | + { |
| 40 | + for (auto &v : order) |
| 41 | + v.clear(); |
| 42 | + } |
| 43 | +}; |
| 44 | + |
| 45 | +// LEAST RECENTLY USED (LRU) ALGORITHM |
| 46 | +class LRUReplacement : public ReplacementPolicy |
| 47 | +{ |
| 48 | + vector<vector<int>> recent; |
| 49 | + |
| 50 | +public: |
| 51 | + LRUReplacement(int sets, int ways) { recent.assign(sets, {}); } |
| 52 | + int chooseVictim(int setIndex, const vector<unsigned long long> & = {}) |
| 53 | + { |
| 54 | + int victim = recent[setIndex].front(); |
| 55 | + recent[setIndex].erase(recent[setIndex].begin()); |
| 56 | + return victim; |
| 57 | + } |
| 58 | + void onAccess(int setIndex, int lineIndex) |
| 59 | + { |
| 60 | + auto &r = recent[setIndex]; |
| 61 | + r.erase(remove(r.begin(), r.end(), lineIndex), r.end()); |
| 62 | + r.push_back(lineIndex); |
| 63 | + } |
| 64 | + void onInsert(int setIndex, int lineIndex) { onAccess(setIndex, lineIndex); } |
| 65 | + void reset() |
| 66 | + { |
| 67 | + for (auto &r : recent) |
| 68 | + r.clear(); |
| 69 | + } |
| 70 | +}; |
| 71 | + |
| 72 | +// LEAST FREQUENTLY USED (LFU) ALGORITHM |
| 73 | +class LFUReplacement : public ReplacementPolicy |
| 74 | +{ |
| 75 | + vector<vector<int>> freq; |
| 76 | + |
| 77 | +public: |
| 78 | + LFUReplacement(int sets, int ways) { freq.assign(sets, vector<int>(ways, 0)); } |
| 79 | + int chooseVictim(int setIndex, const vector<unsigned long long> & = {}) |
| 80 | + { |
| 81 | + int victim = 0, minFreq = freq[setIndex][0]; |
| 82 | + for (int i = 1; i < (int)freq[setIndex].size(); i++) |
| 83 | + if (freq[setIndex][i] < minFreq) |
| 84 | + { |
| 85 | + minFreq = freq[setIndex][i]; |
| 86 | + victim = i; |
| 87 | + } |
| 88 | + return victim; |
| 89 | + } |
| 90 | + void onAccess(int setIndex, int lineIndex) { freq[setIndex][lineIndex]++; } |
| 91 | + void onInsert(int setIndex, int lineIndex) { freq[setIndex][lineIndex] = 1; } |
| 92 | + void reset() |
| 93 | + { |
| 94 | + for (auto &f : freq) |
| 95 | + fill(f.begin(), f.end(), 0); |
| 96 | + } |
| 97 | +}; |
| 98 | + |
| 99 | +// BELADY (Optimal) ALGORITHM |
| 100 | +class BeladyReplacement : public ReplacementPolicy |
| 101 | +{ |
| 102 | + vector<vector<unsigned long long>> *cacheRefs; |
| 103 | + vector<vector<CacheBlock>> *cacheSets; |
| 104 | + vector<unsigned long long> *trace; |
| 105 | + size_t currentIndex; |
| 106 | + |
| 107 | +public: |
| 108 | + BeladyReplacement(int, int, vector<vector<unsigned long long>> *refs, |
| 109 | + vector<vector<CacheBlock>> *sets, |
| 110 | + vector<unsigned long long> *t) |
| 111 | + : cacheRefs(refs), cacheSets(sets), trace(t), currentIndex(0) {} |
| 112 | + |
| 113 | + void setCurrentIndex(size_t idx) { currentIndex = idx; } |
| 114 | + |
| 115 | + int chooseVictim(int setIndex, const vector<unsigned long long> & = {}) override |
| 116 | + { |
| 117 | + auto &set = (*cacheSets)[setIndex]; |
| 118 | + int victim = -1; |
| 119 | + size_t farthest = 0; |
| 120 | + |
| 121 | + for (int i = 0; i < (int)set.size(); i++) |
| 122 | + { |
| 123 | + unsigned long long tag = set[i].tag; |
| 124 | + size_t nextUse = SIZE_MAX; |
| 125 | + |
| 126 | + for (size_t j = currentIndex + 1; j < trace->size(); j++) |
| 127 | + { |
| 128 | + unsigned long long addr = (*trace)[j]; |
| 129 | + unsigned long long block = addr / cacheRefs->at(0).size(); |
| 130 | + unsigned long long tagFuture = block / cacheRefs->size(); |
| 131 | + int setFuture = block % cacheRefs->size(); |
| 132 | + |
| 133 | + if (setFuture == setIndex && tagFuture == tag) |
| 134 | + { |
| 135 | + nextUse = j; |
| 136 | + break; |
| 137 | + } |
| 138 | + } |
| 139 | + if (nextUse == SIZE_MAX) |
| 140 | + return i; // never used again |
| 141 | + if (nextUse > farthest) |
| 142 | + { |
| 143 | + farthest = nextUse; |
| 144 | + victim = i; |
| 145 | + } |
| 146 | + } |
| 147 | + return victim == -1 ? 0 : victim; |
| 148 | + } |
| 149 | + |
| 150 | + void onAccess(int, int) {} |
| 151 | + void onInsert(int, int) {} |
| 152 | + void reset() {} |
| 153 | +}; |
| 154 | + |
| 155 | +//CACHE CLASS |
| 156 | +class Cache |
| 157 | +{ |
| 158 | +private: |
| 159 | + int cacheSize, blockSize, associativity, numSets; |
| 160 | + vector<vector<CacheBlock>> sets; |
| 161 | + ReplacementPolicy *policy; |
| 162 | + unsigned long long hits, misses, accesses; |
| 163 | + string policyName; |
| 164 | + |
| 165 | +public: |
| 166 | + Cache(int c, int b, int a, string policyType, vector<unsigned long long> *trace = nullptr) |
| 167 | + : cacheSize(c), blockSize(b), associativity(a), |
| 168 | + hits(0), misses(0), accesses(0), policyName(policyType) |
| 169 | + { |
| 170 | + numSets = cacheSize / (blockSize * associativity); |
| 171 | + sets.resize(numSets, vector<CacheBlock>(associativity)); |
| 172 | + |
| 173 | + for (auto &c : policyType) |
| 174 | + c = toupper(c); |
| 175 | + if (policyType == "FIFO") |
| 176 | + policy = new FIFOReplacement(numSets, associativity); |
| 177 | + else if (policyType == "LRU") |
| 178 | + policy = new LRUReplacement(numSets, associativity); |
| 179 | + else if (policyType == "LFU") |
| 180 | + policy = new LFUReplacement(numSets, associativity); |
| 181 | + else |
| 182 | + policy = new BeladyReplacement(numSets, associativity, nullptr, &sets, trace); |
| 183 | + } |
| 184 | + |
| 185 | + pair<int, unsigned long long> decode(unsigned long long address) |
| 186 | + { |
| 187 | + unsigned long long blockNumber = address / blockSize; |
| 188 | + int setIndex = blockNumber % numSets; |
| 189 | + unsigned long long tag = blockNumber / numSets; |
| 190 | + return {setIndex, tag}; |
| 191 | + } |
| 192 | + |
| 193 | + bool access(unsigned long long address, size_t index, vector<unsigned long long> *trace = nullptr) |
| 194 | + { |
| 195 | + accesses++; |
| 196 | + auto decoded = decode(address); |
| 197 | + int setIndex = decoded.first; |
| 198 | + unsigned long long tag = decoded.second; |
| 199 | + |
| 200 | + auto &set = sets[setIndex]; |
| 201 | + |
| 202 | + // Check for hit |
| 203 | + for (int i = 0; i < associativity; i++) |
| 204 | + { |
| 205 | + if (set[i].valid && set[i].tag == tag) |
| 206 | + { |
| 207 | + hits++; |
| 208 | + policy->onAccess(setIndex, i); |
| 209 | + return true; |
| 210 | + } |
| 211 | + } |
| 212 | + |
| 213 | + // Miss |
| 214 | + misses++; |
| 215 | + int victim = -1; |
| 216 | + for (int i = 0; i < associativity; i++) |
| 217 | + if (!set[i].valid) |
| 218 | + { |
| 219 | + victim = i; |
| 220 | + break; |
| 221 | + } |
| 222 | + |
| 223 | + if (victim == -1) |
| 224 | + { |
| 225 | + if (policyName == "BELADY") |
| 226 | + { |
| 227 | + ((BeladyReplacement *)policy)->setCurrentIndex(index); |
| 228 | + } |
| 229 | + victim = policy->chooseVictim(setIndex); |
| 230 | + } |
| 231 | + |
| 232 | + set[victim].valid = true; |
| 233 | + set[victim].tag = tag; |
| 234 | + policy->onInsert(setIndex, victim); |
| 235 | + return false; |
| 236 | + } |
| 237 | + |
| 238 | + void printCacheState() |
| 239 | + { |
| 240 | + for (int s = 0; s < numSets; s++) |
| 241 | + { |
| 242 | + cout << "Set " << setw(2) << s << ": "; |
| 243 | + for (int w = 0; w < associativity; w++) |
| 244 | + cout << (sets[s][w].valid ? "[T" + to_string(sets[s][w].tag) + "] " : "[ ] "); |
| 245 | + cout << "\n"; |
| 246 | + } |
| 247 | + } |
| 248 | + |
| 249 | + void showStats() |
| 250 | + { |
| 251 | + double missRate = (double)misses / max(accesses, 1ULL); |
| 252 | + double hitRate = 1.0 - missRate; |
| 253 | + double AMAT = 1 + missRate * 100; // let us assume miss penalty 100 cycles |
| 254 | + |
| 255 | + cout << fixed << setprecision(2); |
| 256 | + cout << "\nAccesses: " << accesses |
| 257 | + << " | Hits: " << hits |
| 258 | + << " | Misses: " << misses << endl; |
| 259 | + cout << "Hit Rate: " << hitRate * 100 << "% Miss Rate: " << missRate * 100 << "%\n"; |
| 260 | + cout << "Average Memory Access Time (AMAT): " << AMAT << " cycles\n"; |
| 261 | + } |
| 262 | +}; |
| 263 | + |
| 264 | +// Simulator starts from here |
| 265 | +class CacheSimulator |
| 266 | +{ |
| 267 | +private: |
| 268 | + int cacheSize, blockSize, associativity; |
| 269 | + string policyName; |
| 270 | + vector<unsigned long long> trace; |
| 271 | + |
| 272 | +public: |
| 273 | + bool loadFromFile(string filename) |
| 274 | + { |
| 275 | + ifstream fin(filename); |
| 276 | + if (!fin.is_open()) |
| 277 | + { |
| 278 | + cerr << "Error: Could not open " << filename << endl; |
| 279 | + return false; |
| 280 | + } |
| 281 | + string token; |
| 282 | + while (fin >> token) |
| 283 | + { |
| 284 | + if (token == "CACHE_SIZE") |
| 285 | + fin >> cacheSize; |
| 286 | + else if (token == "BLOCK_SIZE") |
| 287 | + fin >> blockSize; |
| 288 | + else if (token == "ASSOCIATIVITY") |
| 289 | + fin >> associativity; |
| 290 | + else if (token == "POLICY") |
| 291 | + fin >> policyName; |
| 292 | + else if (token == "ACCESSES") |
| 293 | + { |
| 294 | + unsigned long long addr; |
| 295 | + while (fin >> addr) |
| 296 | + trace.push_back(addr); |
| 297 | + } |
| 298 | + } |
| 299 | + fin.close(); |
| 300 | + return true; |
| 301 | + } |
| 302 | + |
| 303 | + void run() |
| 304 | + { |
| 305 | + Cache cache(cacheSize, blockSize, associativity, policyName, &trace); |
| 306 | + cout << "\nCACHE CONFIGURATION:\n"; |
| 307 | + cout << "Cache Size: " << cacheSize << "B, Block: " << blockSize |
| 308 | + << "B, Assoc: " << associativity |
| 309 | + << "-way, Policy: " << policyName << "\n\n"; |
| 310 | + |
| 311 | + for (size_t i = 0; i < trace.size(); i++) |
| 312 | + { |
| 313 | + bool hit = cache.access(trace[i], i, &trace); |
| 314 | + cout << "Access " << setw(2) << i + 1 |
| 315 | + << " | Addr: " << setw(6) << trace[i] |
| 316 | + << " | " << (hit ? "HIT" : "MISS") << "\n"; |
| 317 | + cache.printCacheState(); |
| 318 | + cout << "---------------------------------------------\n"; |
| 319 | + } |
| 320 | + |
| 321 | + cache.showStats(); |
| 322 | + cout << "\nSimulation Complete.\n"; |
| 323 | + } |
| 324 | +}; |
| 325 | + |
| 326 | +// DRIVER CODE |
| 327 | +int main() |
| 328 | +{ |
| 329 | + CacheSimulator sim; |
| 330 | + if (sim.loadFromFile("cacheData.txt")) |
| 331 | + sim.run(); |
| 332 | + return 0; |
| 333 | +} |
0 commit comments