Skip to content

Commit 5251a56

Browse files
committed
Add ClockOracle eviction algorithm implementation and update related files
1 parent 07128a4 commit 5251a56

4 files changed

Lines changed: 228 additions & 0 deletions

File tree

libCacheSim/bin/cachesim/cache_init.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static inline cache_t *create_cache(const char *trace_path,
4242
{"cacheus", Cacheus_init},
4343
{"clock", Clock_init},
4444
{"clockri", ClockRI_init},
45+
{"clockoracle", ClockOracle_init},
4546
{"clockpro", ClockPro_init},
4647
{"fifo", FIFO_init},
4748
{"fifo-merge", FIFO_Merge_init},

libCacheSim/cache/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set(eviction_sources_c
3333
eviction/Cacheus.c
3434
eviction/Clock.c
3535
eviction/ClockRI.c
36+
eviction/ClockOracle.c
3637
eviction/ClockPro.c
3738
eviction/FIFO.c
3839
eviction/Hyperbolic.c
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
//
2+
// ClockOracle
3+
//
4+
// An oracle-assisted CLOCK that uses next_access_vtime to decide reinsertion.
5+
// During eviction scan, an object is reinserted only if:
6+
// next_access_vtime - current_vtime <= cache_size / miss_ratio
7+
// Objects whose next access is beyond this threshold are evicted.
8+
//
9+
// Requires oracle traces (oracleGeneral / lcs) that provide next_access_vtime.
10+
//
11+
// ClockOracle.c
12+
// libCacheSim
13+
//
14+
15+
#include "dataStructure/hashtable/hashtable.h"
16+
#include "libCacheSim/evictionAlgo.h"
17+
18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif
21+
22+
typedef struct {
23+
cache_obj_t *q_head;
24+
cache_obj_t *q_tail;
25+
26+
int64_t n_miss;
27+
28+
int64_t n_obj_rewritten;
29+
int64_t n_byte_rewritten;
30+
} ClockOracle_params_t;
31+
32+
// ***********************************************************************
33+
// **** ****
34+
// **** function declarations ****
35+
// **** ****
36+
// ***********************************************************************
37+
38+
static void ClockOracle_free(cache_t *cache);
39+
static bool ClockOracle_get(cache_t *cache, const request_t *req);
40+
static cache_obj_t *ClockOracle_find(cache_t *cache, const request_t *req,
41+
bool update_cache);
42+
static cache_obj_t *ClockOracle_insert(cache_t *cache, const request_t *req);
43+
static cache_obj_t *ClockOracle_to_evict(cache_t *cache, const request_t *req);
44+
static void ClockOracle_evict(cache_t *cache, const request_t *req);
45+
static bool ClockOracle_remove(cache_t *cache, obj_id_t obj_id);
46+
47+
// ***********************************************************************
48+
// **** ****
49+
// **** end user facing functions ****
50+
// **** ****
51+
// ***********************************************************************
52+
53+
cache_t *ClockOracle_init(const common_cache_params_t ccache_params,
54+
const char *cache_specific_params) {
55+
cache_t *cache =
56+
cache_struct_init("ClockOracle", ccache_params, cache_specific_params);
57+
cache->cache_init = ClockOracle_init;
58+
cache->cache_free = ClockOracle_free;
59+
cache->get = ClockOracle_get;
60+
cache->find = ClockOracle_find;
61+
cache->insert = ClockOracle_insert;
62+
cache->evict = ClockOracle_evict;
63+
cache->remove = ClockOracle_remove;
64+
cache->can_insert = cache_can_insert_default;
65+
cache->get_n_obj = cache_get_n_obj_default;
66+
cache->get_occupied_byte = cache_get_occupied_byte_default;
67+
cache->to_evict = ClockOracle_to_evict;
68+
cache->obj_md_size = 0;
69+
70+
ClockOracle_params_t *params = my_malloc(ClockOracle_params_t);
71+
memset(params, 0, sizeof(ClockOracle_params_t));
72+
cache->eviction_params = params;
73+
74+
params->q_head = NULL;
75+
params->q_tail = NULL;
76+
params->n_miss = 0;
77+
78+
return cache;
79+
}
80+
81+
static void ClockOracle_free(cache_t *cache) {
82+
my_free(sizeof(ClockOracle_params_t), cache->eviction_params);
83+
cache_struct_free(cache);
84+
}
85+
86+
/**
87+
* @brief custom get that tracks misses for miss ratio computation
88+
*/
89+
static bool ClockOracle_get(cache_t *cache, const request_t *req) {
90+
ClockOracle_params_t *params =
91+
(ClockOracle_params_t *)cache->eviction_params;
92+
93+
cache->n_req += 1;
94+
95+
cache_obj_t *obj = cache->find(cache, req, true);
96+
bool hit = (obj != NULL);
97+
98+
if (!hit) {
99+
params->n_miss += 1;
100+
101+
if (cache->can_insert(cache, req)) {
102+
while (cache->get_occupied_byte(cache) + req->obj_size +
103+
cache->obj_md_size >
104+
cache->cache_size) {
105+
cache->evict(cache, req);
106+
}
107+
cache->insert(cache, req);
108+
}
109+
}
110+
111+
return hit;
112+
}
113+
114+
// ***********************************************************************
115+
// **** ****
116+
// **** developer facing APIs (used by cache developer) ****
117+
// **** ****
118+
// ***********************************************************************
119+
120+
static cache_obj_t *ClockOracle_find(cache_t *cache, const request_t *req,
121+
bool update_cache) {
122+
cache_obj_t *obj = cache_find_base(cache, req, update_cache);
123+
if (obj != NULL && update_cache) {
124+
obj->next_access_vtime = req->next_access_vtime;
125+
}
126+
return obj;
127+
}
128+
129+
static cache_obj_t *ClockOracle_insert(cache_t *cache, const request_t *req) {
130+
ClockOracle_params_t *params =
131+
(ClockOracle_params_t *)cache->eviction_params;
132+
133+
cache_obj_t *obj = cache_insert_base(cache, req);
134+
prepend_obj_to_head(&params->q_head, &params->q_tail, obj);
135+
136+
obj->next_access_vtime = req->next_access_vtime;
137+
138+
return obj;
139+
}
140+
141+
static cache_obj_t *ClockOracle_to_evict(cache_t *cache, const request_t *req) {
142+
ClockOracle_params_t *params =
143+
(ClockOracle_params_t *)cache->eviction_params;
144+
return params->q_tail;
145+
}
146+
147+
/**
148+
* @brief evict using oracle information
149+
*
150+
* Scan from the tail. For each object, compute the reinsertion threshold:
151+
* threshold = cache_size / miss_ratio
152+
* If next_access_vtime - current_vtime > threshold, evict the object.
153+
* Otherwise, reinsert it to the head.
154+
*
155+
* Objects with next_access_vtime == INT64_MAX (no future access) are always
156+
* evicted.
157+
*/
158+
static void ClockOracle_evict(cache_t *cache, const request_t *req) {
159+
ClockOracle_params_t *params =
160+
(ClockOracle_params_t *)cache->eviction_params;
161+
162+
/* compute the reinsertion threshold: cache_size / miss_ratio
163+
* miss_ratio = n_miss / n_req, so threshold = cache_size * n_req / n_miss
164+
* when n_miss == 0, use cache_size as the threshold (conservative) */
165+
int64_t threshold;
166+
if (params->n_miss > 0) {
167+
threshold = (int64_t)((double)cache->cache_size * (double)cache->n_req /
168+
(double)params->n_miss);
169+
} else {
170+
threshold = cache->cache_size;
171+
}
172+
173+
/* scan at most n_obj objects to avoid infinite loop */
174+
int64_t n_scanned = 0;
175+
cache_obj_t *obj_to_evict = params->q_tail;
176+
while (obj_to_evict != NULL && n_scanned < cache->n_obj) {
177+
int64_t reuse_dist = obj_to_evict->next_access_vtime - cache->n_req;
178+
n_scanned++;
179+
180+
/* evict if no future access or reuse distance exceeds threshold */
181+
if (obj_to_evict->next_access_vtime == INT64_MAX || reuse_dist > threshold) {
182+
break;
183+
}
184+
185+
/* reinsert: move to head */
186+
params->n_obj_rewritten += 1;
187+
params->n_byte_rewritten += obj_to_evict->obj_size;
188+
move_obj_to_head(&params->q_head, &params->q_tail, obj_to_evict);
189+
obj_to_evict = params->q_tail;
190+
}
191+
192+
/* safety: if everything was reinserted, evict the tail */
193+
if (obj_to_evict == NULL) {
194+
obj_to_evict = params->q_tail;
195+
}
196+
197+
remove_obj_from_list(&params->q_head, &params->q_tail, obj_to_evict);
198+
cache_evict_base(cache, obj_to_evict, true);
199+
}
200+
201+
static void ClockOracle_remove_obj(cache_t *cache, cache_obj_t *obj) {
202+
ClockOracle_params_t *params =
203+
(ClockOracle_params_t *)cache->eviction_params;
204+
205+
DEBUG_ASSERT(obj != NULL);
206+
remove_obj_from_list(&params->q_head, &params->q_tail, obj);
207+
cache_remove_obj_base(cache, obj, true);
208+
}
209+
210+
static bool ClockOracle_remove(cache_t *cache, obj_id_t obj_id) {
211+
cache_obj_t *obj = hashtable_find_obj_id(cache->hashtable, obj_id);
212+
if (obj == NULL) {
213+
return false;
214+
}
215+
216+
ClockOracle_remove_obj(cache, obj);
217+
218+
return true;
219+
}
220+
221+
#ifdef __cplusplus
222+
}
223+
#endif

libCacheSim/include/libCacheSim/evictionAlgo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ cache_t *Clock_init(const common_cache_params_t ccache_params,
8080
cache_t *ClockRI_init(const common_cache_params_t ccache_params,
8181
const char *cache_specific_params);
8282

83+
cache_t *ClockOracle_init(const common_cache_params_t ccache_params,
84+
const char *cache_specific_params);
85+
8386
cache_t *ClockPro_init(const common_cache_params_t ccache_params,
8487
const char *cache_specific_params);
8588

0 commit comments

Comments
 (0)