Skip to content

Commit bc152ae

Browse files
committed
Add write amplification statistics and update eviction algorithms for enhanced reporting
1 parent 5251a56 commit bc152ae

File tree

6 files changed

+502
-49
lines changed

6 files changed

+502
-49
lines changed

libCacheSim/bin/cachesim/sim.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "libCacheSim/cache.h"
2+
#include "libCacheSim/evictionAlgo.h"
23
#include "libCacheSim/reader.h"
34
#include "utils/include/mymath.h"
45
#include "utils/include/mystr.h"
@@ -103,6 +104,52 @@ void simulate(reader_t *reader, cache_t *cache, int report_interval,
103104
if (show_cost)
104105
n += snprintf(output_str + n, sizeof(output_str) - n,
105106
", cost saving ratio %.4lf", cost_saving_ratio);
107+
108+
/* print write amplification stats for algorithms that track them */
109+
if (cache->cache_init == Clock_init) {
110+
Clock_params_t *p = (Clock_params_t *)cache->eviction_params;
111+
double write_ratio = req_byte > 0
112+
? (double)(miss_byte + p->n_byte_rewritten) / (double)req_byte : 0.0;
113+
n += snprintf(output_str + n, sizeof(output_str) - n,
114+
", write ratio %.4lf", write_ratio);
115+
} else if (cache->cache_init == ClockRI_init) {
116+
ClockRI_params_t *p = (ClockRI_params_t *)cache->eviction_params;
117+
double write_ratio = req_byte > 0
118+
? (double)(miss_byte + p->n_byte_rewritten) / (double)req_byte : 0.0;
119+
n += snprintf(output_str + n, sizeof(output_str) - n,
120+
", write ratio %.4lf", write_ratio);
121+
} else if (cache->cache_init == ClockOracle_init) {
122+
ClockOracle_params_t *p = (ClockOracle_params_t *)cache->eviction_params;
123+
double write_ratio = req_byte > 0
124+
? (double)(miss_byte + p->n_byte_rewritten) / (double)req_byte : 0.0;
125+
n += snprintf(output_str + n, sizeof(output_str) - n,
126+
", write ratio %.4lf", write_ratio);
127+
} else if (cache->cache_init == LRU_init) {
128+
LRU_params_t *p = (LRU_params_t *)cache->eviction_params;
129+
double write_ratio = req_byte > 0
130+
? (double)(miss_byte + p->n_byte_promoted) / (double)req_byte : 0.0;
131+
n += snprintf(output_str + n, sizeof(output_str) - n,
132+
", write ratio %.4lf", write_ratio);
133+
} else if (cache->cache_init == FIFO_init) {
134+
/* FIFO: flash writes = miss bytes only */
135+
double write_ratio = byte_miss_ratio;
136+
n += snprintf(output_str + n, sizeof(output_str) - n,
137+
", write ratio %.4lf", write_ratio);
138+
} else if (cache->cache_init == S3FIFO_init) {
139+
/* S3FIFO: flash writes = miss bytes + promotion bytes + reinsertion bytes */
140+
S3FIFO_params_t *p = (S3FIFO_params_t *)cache->eviction_params;
141+
double write_ratio = req_byte > 0
142+
? (double)(miss_byte + p->n_byte_promoted + p->n_byte_rewritten)
143+
/ (double)req_byte : 0.0;
144+
n += snprintf(output_str + n, sizeof(output_str) - n,
145+
", write ratio %.4lf", write_ratio);
146+
} else if (cache->cache_init == Sieve_init) {
147+
/* Sieve: no data movement, flash writes = miss bytes only */
148+
double write_ratio = byte_miss_ratio;
149+
n += snprintf(output_str + n, sizeof(output_str) - n,
150+
", write ratio %.4lf", write_ratio);
151+
}
152+
106153
snprintf(output_str + n, sizeof(output_str) - n, ", throughput %.2lf MQPS\n",
107154
(double)req_cnt / 1000000.0 / runtime);
108155
printf("%s", output_str);

libCacheSim/cache/eviction/ClockOracle.c

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
//
22
// ClockOracle
33
//
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.
4+
// An oracle-assisted CLOCK that combines the visited bit with oracle
5+
// reuse distance to decide reinsertion.
6+
//
7+
// An object is reinserted only if BOTH conditions hold:
8+
// 1. The visited bit is set (object was accessed since last eviction scan)
9+
// 2. next_access_vtime - current_vtime <= cache_size / miss_ratio
10+
//
11+
// The visited bit is cleared on reinsertion (like standard CLOCK).
12+
// Objects failing either condition are evicted.
813
//
914
// Requires oracle traces (oracleGeneral / lcs) that provide next_access_vtime.
1015
//
@@ -19,16 +24,6 @@
1924
extern "C" {
2025
#endif
2126

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-
3227
// ***********************************************************************
3328
// **** ****
3429
// **** function declarations ****
@@ -121,6 +116,8 @@ static cache_obj_t *ClockOracle_find(cache_t *cache, const request_t *req,
121116
bool update_cache) {
122117
cache_obj_t *obj = cache_find_base(cache, req, update_cache);
123118
if (obj != NULL && update_cache) {
119+
/* set visited bit */
120+
obj->clock.freq = 1;
124121
obj->next_access_vtime = req->next_access_vtime;
125122
}
126123
return obj;
@@ -133,6 +130,8 @@ static cache_obj_t *ClockOracle_insert(cache_t *cache, const request_t *req) {
133130
cache_obj_t *obj = cache_insert_base(cache, req);
134131
prepend_obj_to_head(&params->q_head, &params->q_tail, obj);
135132

133+
/* new objects start with visited bit clear */
134+
obj->clock.freq = 0;
136135
obj->next_access_vtime = req->next_access_vtime;
137136

138137
return obj;
@@ -145,23 +144,21 @@ static cache_obj_t *ClockOracle_to_evict(cache_t *cache, const request_t *req) {
145144
}
146145

147146
/**
148-
* @brief evict using oracle information
147+
* @brief evict using oracle + visited bit
149148
*
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.
149+
* Scan from the tail. An object is reinserted only if BOTH:
150+
* 1. visited bit is set (freq >= 1)
151+
* 2. next_access_vtime - current_vtime <= cache_size / miss_ratio
154152
*
155-
* Objects with next_access_vtime == INT64_MAX (no future access) are always
156-
* evicted.
153+
* On reinsertion, the visited bit is cleared.
154+
* Objects failing either condition are evicted.
157155
*/
158156
static void ClockOracle_evict(cache_t *cache, const request_t *req) {
159157
ClockOracle_params_t *params =
160158
(ClockOracle_params_t *)cache->eviction_params;
161159

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) */
160+
/* threshold = cache_size / miss_ratio = cache_size * n_req / n_miss
161+
* when n_miss == 0, use cache_size as the threshold */
165162
int64_t threshold;
166163
if (params->n_miss > 0) {
167164
threshold = (int64_t)((double)cache->cache_size * (double)cache->n_req /
@@ -170,23 +167,31 @@ static void ClockOracle_evict(cache_t *cache, const request_t *req) {
170167
threshold = cache->cache_size;
171168
}
172169

173-
/* scan at most n_obj objects to avoid infinite loop */
174-
int64_t n_scanned = 0;
170+
175171
cache_obj_t *obj_to_evict = params->q_tail;
172+
int64_t n_scanned = 0;
176173
while (obj_to_evict != NULL && n_scanned < cache->n_obj) {
177-
int64_t reuse_dist = obj_to_evict->next_access_vtime - cache->n_req;
178174
n_scanned++;
179175

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;
176+
bool no_future_access = (obj_to_evict->next_access_vtime == -1 ||
177+
obj_to_evict->next_access_vtime == INT64_MAX);
178+
bool visited = (obj_to_evict->clock.freq >= 1);
179+
int64_t reuse_dist = obj_to_evict->next_access_vtime - cache->n_req;
180+
bool within_threshold = (!no_future_access && reuse_dist <= threshold);
181+
182+
/* reinsert only if visited AND within threshold */
183+
if (visited && within_threshold) {
184+
/* clear visited bit, reinsert to head */
185+
obj_to_evict->clock.freq = 0;
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+
continue;
183191
}
184192

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;
193+
/* evict: either not visited, no future access, or too far away */
194+
break;
190195
}
191196

192197
/* safety: if everything was reinserted, evict the tail */

libCacheSim/cache/eviction/S3FIFO.c

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,6 @@
3636
extern "C" {
3737
#endif
3838

39-
typedef struct {
40-
cache_t *small_fifo;
41-
cache_t *ghost_fifo;
42-
cache_t *main_fifo;
43-
bool hit_on_ghost;
44-
45-
int move_to_main_threshold;
46-
double small_size_ratio;
47-
double ghost_size_ratio;
48-
49-
bool has_evicted;
50-
request_t *req_local;
51-
} S3FIFO_params_t;
52-
5339
static const char *DEFAULT_CACHE_PARAMS =
5440
"small-size-ratio=0.10,ghost-size-ratio=0.90,move-to-main-threshold=2";
5541

@@ -323,6 +309,8 @@ static void S3FIFO_evict_small(cache_t *cache, const request_t *req) {
323309
copy_cache_obj_to_request(params->req_local, obj_to_evict);
324310

325311
if (obj_to_evict->S3FIFO.freq >= params->move_to_main_threshold) {
312+
params->n_obj_promoted += 1;
313+
params->n_byte_promoted += obj_to_evict->obj_size;
326314
main_fifo->insert(main_fifo, params->req_local);
327315
} else {
328316
// insert to ghost
@@ -349,6 +337,8 @@ static void S3FIFO_evict_main(cache_t *cache, const request_t *req) {
349337
int freq = obj_to_evict->S3FIFO.freq;
350338
copy_cache_obj_to_request(params->req_local, obj_to_evict);
351339
if (freq >= 1) {
340+
params->n_obj_rewritten += 1;
341+
params->n_byte_rewritten += obj_to_evict->obj_size;
352342
// we need to evict first because the object to insert has the same obj_id
353343
main_fifo->remove(main_fifo, obj_to_evict->obj_id);
354344
obj_to_evict = NULL;

libCacheSim/include/libCacheSim/evictionAlgo.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ typedef struct {
5656
int64_t n_byte_rewritten;
5757
} ClockRI_params_t;
5858

59+
typedef struct {
60+
cache_obj_t *q_head;
61+
cache_obj_t *q_tail;
62+
63+
int64_t n_miss;
64+
65+
int64_t n_obj_rewritten;
66+
int64_t n_byte_rewritten;
67+
} ClockOracle_params_t;
68+
5969
cache_t *ARC_init(const common_cache_params_t ccache_params,
6070
const char *cache_specific_params);
6171

@@ -159,6 +169,25 @@ cache_t *RandomTwo_init(const common_cache_params_t ccache_params,
159169
cache_t *Random_init(const common_cache_params_t ccache_params,
160170
const char *cache_specific_params);
161171

172+
typedef struct {
173+
cache_t *small_fifo;
174+
cache_t *ghost_fifo;
175+
cache_t *main_fifo;
176+
bool hit_on_ghost;
177+
178+
int move_to_main_threshold;
179+
double small_size_ratio;
180+
double ghost_size_ratio;
181+
182+
bool has_evicted;
183+
request_t *req_local;
184+
185+
int64_t n_obj_promoted;
186+
int64_t n_byte_promoted;
187+
int64_t n_obj_rewritten;
188+
int64_t n_byte_rewritten;
189+
} S3FIFO_params_t;
190+
162191
cache_t *S3FIFO_init(const common_cache_params_t ccache_params,
163192
const char *cache_specific_params);
164193

0 commit comments

Comments
 (0)