-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.cc
165 lines (128 loc) · 5.12 KB
/
cache.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "cache.hh"
#include <cassert>
#include "DirectMappedCache.hh"
#include "InfiniteCache.hh"
#include "SetAssociativeCache.hh"
static constexpr unsigned int nbits(const uint64_t n) {
return n == 1 ? 0 : 1 + nbits(n >> 1);
}
// ------
CacheEvents& CacheEvents::operator+=(const CacheEvents& rhs) {
this->hits += rhs.hits;
this->misses += rhs.misses;
this->evictions += rhs.evictions;
return *this;
}
bool CacheEvents::hit() const { return misses == 0; }
// ------
CacheAddress::CacheAddress(uint64_t address, uint64_t cache_size, int line_size,
int set_size) {
const unsigned int block_bits { nbits(line_size) };
const unsigned int index_bits { nbits(cache_size) - nbits(line_size) -
nbits(set_size) };
assert(index_bits < 62 && "Address index exceed 64-bit address space");
block = address & ((1 << block_bits) - 1);
index = (address >> block_bits) & ((1 << index_bits) - 1);
tag = address >> (block_bits + index_bits);
}
CacheAddress::CacheAddress(uint64_t address, const CacheConfig& config)
: CacheAddress(address, config.size, config.line_size, config.set_size) { }
CacheAddress::CacheAddress(uint64_t address, const Cache& cache)
: CacheAddress(address, cache.getSize(), cache.getLineSize(), cache.getSetSize()) { }
// ------
void CacheEntry::set(uint64_t tag, uint64_t timestamp) {
this->tag = tag;
this->loaded_at = timestamp;
this->valid = true;
this->age = 0;
}
// ------
Cache::Cache(const uint64_t size, const int line_size, const int set_size,
const std::shared_ptr<const Clock> clock)
: size(size), line_size(line_size), set_size(set_size), clock_(clock) {
if (size % line_size != 0)
throw std::invalid_argument("Line size does not divide cache size");
if (size % set_size != 0)
throw std::invalid_argument("Set size does not divide cache size");
if ((size & (size - 1)) != 0)
throw std::invalid_argument("Cache size is not a power of 2");
}
Cache::Cache(const CacheConfig& config, const std::shared_ptr<const Clock> clock)
: Cache(config.size, config.line_size, config.set_size, clock) { }
Cache::~Cache() { }
const CacheAddress Cache::split_address(const uint64_t address) const {
return CacheAddress(address, *this);
}
void Cache::log_eviction(uint64_t loaded_at) {
lifetimes[clock_->current_cycle() - loaded_at]++;
}
CacheEvents Cache::touch(const uint64_t address, const int size) {
CacheEvents events {};
uint64_t next_address { address };
int remaining_size { size };
while (remaining_size > 0) {
// Find the first cache line this request touches
auto const& cache_address = split_address(next_address);
events += touch(cache_address);
// Skip over the remaining bytes in this same cache line
const unsigned int covered_bytes = line_size - cache_address.block;
remaining_size -= covered_bytes;
next_address += covered_bytes;
}
return events;
}
CacheEvents Cache::touch(const SizedAccess& access) {
return touch(access.address, access.size);
}
CacheEvents Cache::touch(const MemoryRequest& request) {
return touch(request.address, request.size);
}
CacheEvents Cache::touch(const std::vector<uint64_t>& addresses) {
CacheEvents events {};
for (auto const& a : addresses) events += touch(a);
return events;
}
CacheEvents Cache::touch(const std::vector<CacheAddress>& addresses) {
CacheEvents events {};
for (auto const& a : addresses) events += touch(a);
return events;
}
CacheEvents Cache::touch(const std::vector<SizedAccess>& accesses) {
CacheEvents events {};
for (auto const& a : accesses) events += touch(a);
return events;
}
CacheEvents Cache::touch(const std::vector<MemoryRequest>& requests) {
CacheEvents events {};
for (auto const& r : requests) events += touch(r);
return events;
}
uint64_t Cache::getSize() const { return size; }
int Cache::getLineSize() const { return line_size; }
int Cache::getSetSize() const { return set_size; }
uint64_t Cache::getHits() const { return hits; }
uint64_t Cache::getMisses() const { return misses; }
uint64_t Cache::getTotalAccesses() const { return hits + misses; }
uint64_t Cache::getEvictions() const { return evictions; }
std::unique_ptr<std::map<uint64_t, uint64_t>> Cache::getLifetimes() const {
// The `lifetime` map only has data items already evicted, so make a copy and add data
// for everything still in the cache
auto merged_map = getActiveLifetimes();
for (const auto& [life, count] : lifetimes) (*merged_map)[life] += count;
return merged_map;
}
std::unique_ptr<Cache> Cache::make_cache(const CacheConfig& config,
const std::shared_ptr<const Clock> clock) {
switch (config.type) {
case CacheType::Infinite:
return std::make_unique<InfiniteCache>(clock);
case CacheType::DirectMapped:
return std::make_unique<DirectMappedCache>(config, clock);
case CacheType::SetAssociative:
return std::make_unique<SetAssociativeCache>(config, clock);
default:
throw std::invalid_argument("Unknown cache type");
}
}
NotImplementedException::NotImplementedException()
: std::logic_error("Not implemented") { }