Skip to content

Commit 23b8744

Browse files
authored
Merge pull request #66 from LONECODER1/feature/cacheSimulator
cache simulator added
2 parents 9a64104 + dcb7ba3 commit 23b8744

File tree

4 files changed

+392
-0
lines changed

4 files changed

+392
-0
lines changed

Src/Cache_Simulator/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# C++ Cache Memory Simulator
2+
3+
## Overview
4+
This project is a **Cache Memory Simulator implemented in C++**.
5+
It emulates how a **CPU cache** interacts with **main memory** and supports multiple **cache replacement algorithms** to analyze cache performance.
6+
7+
The simulator is designed using **Object-Oriented Programming (OOP)** principles, making the code **modular, maintainable, and easy to extend** for learning or experimentation.
8+
9+
---
10+
11+
## Features
12+
- Simulates real-world **cache memory behavior**
13+
- Supports major cache replacement strategies:
14+
- **FIFO (First-In First-Out)**
15+
- **LRU (Least Recently Used)**
16+
- **LFU (Least Frequently Used)**
17+
- **Belady’s Optimal Algorithm** *(for theoretical comparison)*
18+
- Allows **custom cache configuration**:
19+
- Cache size
20+
- Block size
21+
- Associativity (n-way)
22+
- Reads **variable memory access sequences** from an input file (`cacheData.txt`)
23+
- Displays **cache state after every access**
24+
- Tracks:
25+
- Cache **hits** and **misses**
26+
- **Hit/Miss rates**
27+
- **Average Memory Access Time (AMAT)**
28+
29+
---
30+
31+
## Requirements
32+
- **C++ Implementation**: Entirely written in C++
33+
- **Object-Oriented Design**: Uses classes like `CacheBlock`, `Cache`, and `ReplacementPolicy`
34+
- **Clean Code Practices**:
35+
- Structured and well-commented code
36+
- Modular design with inheritance for replacement policies
37+
- Proper statistics and performance tracking
38+
39+
---

Src/Cache_Simulator/cacheData.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CACHE_SIZE 16384
2+
BLOCK_SIZE 64
3+
ASSOCIATIVITY 4
4+
POLICY BELADY
5+
ACCESSES
6+
0
7+
64
8+
128
9+
0
10+
64
11+
256
12+
128
13+
320
14+
0
15+
64
16+
384
17+
448
18+
0
19+
512
20+
64
Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
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+
}
289 KB
Binary file not shown.

0 commit comments

Comments
 (0)