Skip to content

Commit ebf086e

Browse files
committed
Refactor FIFO_Reinsertion eviction algorithm to use byte-based metrics and update related parameters
1 parent 0da6555 commit ebf086e

File tree

3 files changed

+139
-64
lines changed

3 files changed

+139
-64
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ sftp-config.json
2222
.lint-logs/
2323
# Python wheels
2424
*.whl
25+
2024_google/*

libCacheSim/bin/cachesim/cache_init.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ static inline cache_t *create_cache(const char *trace_path,
4646
{"clockpro", ClockPro_init},
4747
{"fifo", FIFO_init},
4848
{"fifo-merge", FIFO_Merge_init},
49-
{"fifo-reinsertion", Clock_init},
50-
{"fifomerge", FIFO_Merge_init},
49+
{"fifo_merge", FIFO_Merge_init},
50+
{"fifo-reinsertion", FIFO_Reinsertion_init},
51+
{"fifo_reinsertion", FIFO_Reinsertion_init},
5152
{"flashProb", flashProb_init},
5253
{"gdsf", GDSF_init},
5354
{"lhd", LHD_init},

libCacheSim/cache/eviction/FIFO_Reinsertion.c

Lines changed: 135 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,22 @@ typedef struct FIFO_Reinsertion_params {
4040
cache_obj_t *q_tail;
4141

4242
// points to the eviction position
43-
cache_obj_t *next_to_merge;
44-
// the number of object to examine at each eviction
45-
int n_exam_obj;
46-
// of the n_exam_obj, we keep n_keep_obj and evict the rest
47-
int n_keep_obj;
48-
// used to sort the n_exam_obj objects
43+
cache_obj_t *next_to_exam;
44+
// the number of bytes to examine at each eviction round
45+
int64_t n_exam_byte;
46+
// of the n_exam_byte examined, we keep n_keep_byte and evict the rest
47+
int64_t n_keep_byte;
48+
// used to sort the objects collected in the current batch
4949
struct sort_list_node *metric_list;
50-
// the policy to determine the n_keep_obj objects
50+
// current allocated capacity (in entries) of metric_list
51+
int metric_list_capacity;
52+
// the policy to determine which objects to keep
5153
retain_policy_t retain_policy;
54+
int pos_in_metric_list;
55+
// number of objects in the eviction portion of the current sorted batch
56+
int n_collected_in_batch;
57+
// number of bytes already evicted from the current sorted batch
58+
int64_t bytes_evicted_in_batch;
5259

5360
int64_t n_obj_rewritten;
5461
int64_t n_byte_rewritten;
@@ -118,24 +125,30 @@ cache_t *FIFO_Reinsertion_init(const common_cache_params_t ccache_params,
118125
memset(params, 0, sizeof(FIFO_Reinsertion_params_t));
119126
cache->eviction_params = params;
120127

121-
params->n_exam_obj = 100;
122-
params->n_keep_obj = params->n_exam_obj / 5;
128+
params->n_exam_byte = 100LL * 1024 * 1024; // 100 MB
129+
params->n_keep_byte = 25LL * 1024 * 1024; // 25 MB
123130
params->retain_policy = RETAIN_POLICY_RECENCY;
124-
params->next_to_merge = NULL;
131+
params->next_to_exam = NULL;
125132
params->q_head = NULL;
126133
params->q_tail = NULL;
134+
params->pos_in_metric_list = INT32_MAX;
135+
params->n_collected_in_batch = 0;
136+
params->bytes_evicted_in_batch = 0;
127137

128138
if (cache_specific_params != NULL) {
129139
FIFO_Reinsertion_parse_params(cache, cache_specific_params);
130140
}
131141

132-
assert(params->n_exam_obj > 0 && params->n_keep_obj >= 0);
133-
assert(params->n_keep_obj <= params->n_exam_obj);
142+
assert(params->n_exam_byte > 0 && params->n_keep_byte >= 0);
143+
assert(params->n_keep_byte <= params->n_exam_byte);
134144

135145
snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "FIFO_Reinsertion_%s-%.4lf",
136146
retain_policy_names[params->retain_policy],
137-
(double)params->n_keep_obj / params->n_exam_obj);
138-
params->metric_list = my_malloc_n(struct sort_list_node, params->n_exam_obj);
147+
(double)params->n_keep_byte / (double)params->n_exam_byte);
148+
// initial capacity guess; grows on demand in the eviction routine
149+
params->metric_list_capacity = 1024;
150+
params->metric_list =
151+
my_malloc_n(struct sort_list_node, params->metric_list_capacity);
139152

140153
return cache;
141154
}
@@ -148,7 +161,7 @@ cache_t *FIFO_Reinsertion_init(const common_cache_params_t ccache_params,
148161
static void FIFO_Reinsertion_free(cache_t *cache) {
149162
FIFO_Reinsertion_params_t *params =
150163
(FIFO_Reinsertion_params_t *)cache->eviction_params;
151-
my_free(sizeof(struct sort_list_node) * params->n_exam_obj,
164+
my_free(sizeof(struct sort_list_node) * params->metric_list_capacity,
152165
params->metric_list);
153166
my_free(sizeof(FIFO_Reinsertion_params_t), params);
154167
cache_struct_free(cache);
@@ -259,60 +272,95 @@ static cache_obj_t *FIFO_Reinsertion_to_evict(cache_t *cache,
259272
static void FIFO_Reinsertion_evict(cache_t *cache, const request_t *req) {
260273
FIFO_Reinsertion_params_t *params =
261274
(FIFO_Reinsertion_params_t *)cache->eviction_params;
262-
263-
// collect metric for n_exam obj, we will keep objects with larger metric
264-
int n_loop = 0;
265-
cache_obj_t *cache_obj = params->next_to_merge;
266-
if (cache_obj == NULL) {
267-
params->next_to_merge = params->q_tail;
268-
cache_obj = params->q_tail;
269-
n_loop = 1;
275+
cache_obj_t *cache_obj = NULL;
276+
// Collect ~n_exam_byte objects, sort them, then:
277+
// - reinsert the keep-set to head immediately (no cache-size change)
278+
// - store the evict-set and drain it one object per call
279+
const int64_t bytes_to_evict_per_batch =
280+
params->n_exam_byte - params->n_keep_byte;
281+
282+
if (params->pos_in_metric_list < params->n_collected_in_batch &&
283+
params->bytes_evicted_in_batch < bytes_to_evict_per_batch) {
284+
cache_obj = params->metric_list[params->pos_in_metric_list++].cache_obj;
285+
params->bytes_evicted_in_batch += cache_obj->obj_size;
286+
remove_obj_from_list(&params->q_head, &params->q_tail, cache_obj);
287+
cache_evict_base(cache, cache_obj, true);
288+
return;
270289
}
271290

272-
if (cache->n_obj <= params->n_exam_obj) {
273-
// just evict one object
274-
cache_obj = params->next_to_merge->queue.prev;
275-
FIFO_Reinsertion_remove_obj(cache, params->next_to_merge);
276-
params->next_to_merge = cache_obj;
277-
291+
if (cache->get_occupied_byte(cache) <= params->n_exam_byte) {
292+
// cache is too small for a useful merge - fall back to plain FIFO
293+
cache_obj = params->q_tail;
294+
params->next_to_exam = NULL;
295+
remove_obj_from_list(&params->q_head, &params->q_tail, cache_obj);
296+
cache_evict_base(cache, cache_obj, true);
278297
return;
279298
}
280299

281-
for (int i = 0; i < params->n_exam_obj; i++) {
282-
assert(cache_obj != NULL);
283-
params->metric_list[i].metric = retain_metric(cache, cache_obj);
284-
params->metric_list[i].cache_obj = cache_obj;
285-
cache_obj = cache_obj->queue.prev;
286-
287-
// TODO: wrap back to the head of the list early before reaching the end of
288-
// the list
300+
// collect ~n_exam_byte worth of objects from the merge cursor
301+
int n_loop = 0;
302+
int64_t bytes_collected = 0;
303+
int n_collected = 0;
304+
cache_obj = params->next_to_exam;
305+
while (bytes_collected < params->n_exam_byte) {
289306
if (cache_obj == NULL) {
290307
cache_obj = params->q_tail;
291-
DEBUG_ASSERT(n_loop++ <= 2);
308+
n_loop += 1;
309+
DEBUG_ASSERT(n_loop <= 2);
292310
}
311+
312+
// grow metric_list if needed
313+
if (n_collected >= params->metric_list_capacity) {
314+
int new_capacity = params->metric_list_capacity * 2;
315+
struct sort_list_node *new_list =
316+
my_malloc_n(struct sort_list_node, new_capacity);
317+
memcpy(new_list, params->metric_list,
318+
sizeof(struct sort_list_node) * params->metric_list_capacity);
319+
my_free(sizeof(struct sort_list_node) * params->metric_list_capacity,
320+
params->metric_list);
321+
params->metric_list = new_list;
322+
params->metric_list_capacity = new_capacity;
323+
}
324+
325+
params->metric_list[n_collected].metric = retain_metric(cache, cache_obj);
326+
params->metric_list[n_collected].cache_obj = cache_obj;
327+
bytes_collected += cache_obj->obj_size;
328+
n_collected += 1;
329+
cache_obj = cache_obj->queue.prev;
293330
}
294-
params->next_to_merge = cache_obj;
331+
params->next_to_exam = cache_obj;
295332

296-
// sort metrics
297-
qsort(params->metric_list, params->n_exam_obj, sizeof(struct sort_list_node),
333+
// sort by metric ascending - low metric (least worth keeping) first
334+
qsort(params->metric_list, n_collected, sizeof(struct sort_list_node),
298335
cmp_list_node);
299336

300-
// remove objects
301-
int n_evict = params->n_exam_obj - params->n_keep_obj;
302-
for (int i = 0; i < n_evict; i++) {
303-
cache_obj = params->metric_list[i].cache_obj;
304-
FIFO_Reinsertion_remove_obj(cache, cache_obj);
337+
// find the split point: evict [0..n_evict), reinsert [n_evict..n_collected)
338+
int n_evict = 0;
339+
int64_t bytes_evict_counted = 0;
340+
while (n_evict < n_collected && bytes_evict_counted < bytes_to_evict_per_batch) {
341+
bytes_evict_counted += params->metric_list[n_evict].cache_obj->obj_size;
342+
n_evict++;
305343
}
306344

307-
for (int i = n_evict; i < params->n_exam_obj; i++) {
345+
// reinsert the keep-set to head now (size unchanged, so safe to do eagerly)
346+
for (int i = n_evict; i < n_collected; i++) {
308347
cache_obj = params->metric_list[i].cache_obj;
309348
move_obj_to_head(&params->q_head, &params->q_tail, cache_obj);
310349
cache_obj->FIFO_Reinsertion.freq =
311350
(cache_obj->FIFO_Reinsertion.freq + 1) / 2;
312-
313351
params->n_obj_rewritten += 1;
314352
params->n_byte_rewritten += cache_obj->obj_size;
315353
}
354+
355+
// begin draining the evict-set one object per call
356+
params->n_collected_in_batch = n_evict;
357+
params->bytes_evicted_in_batch = 0;
358+
params->pos_in_metric_list = 1;
359+
360+
cache_obj = params->metric_list[0].cache_obj;
361+
params->bytes_evicted_in_batch += cache_obj->obj_size;
362+
remove_obj_from_list(&params->q_head, &params->q_tail, cache_obj);
363+
cache_evict_base(cache, cache_obj, true);
316364
}
317365

318366
static void FIFO_Reinsertion_remove_obj(cache_t *cache, cache_obj_t *obj) {
@@ -356,20 +404,51 @@ static bool FIFO_Reinsertion_remove(cache_t *cache, obj_id_t obj_id) {
356404
static const char *FIFO_Reinsertion_current_params(
357405
FIFO_Reinsertion_params_t *params) {
358406
static __thread char params_str[128];
359-
snprintf(params_str, 128, "n-exam=%d, n-keep=%d, retain-policy=%s",
360-
params->n_exam_obj, params->n_keep_obj,
407+
snprintf(params_str, 128,
408+
"n-exam-byte=%lld, n-keep-byte=%lld, retain-policy=%s",
409+
(long long)params->n_exam_byte, (long long)params->n_keep_byte,
361410
retain_policy_names[params->retain_policy]);
362411
return params_str;
363412
}
364413

414+
/* Parse a human-readable byte size like "100", "100KB", "100MB", "2GB".
415+
* Suffix is case-insensitive; binary (1024) units are used.
416+
* Returns the parsed value, or aborts on parse error. */
417+
static int64_t FIFO_Reinsertion_parse_byte_size(const char *value) {
418+
char *end = NULL;
419+
int64_t n = (int64_t)strtoll(value, &end, 0);
420+
if (end == value) {
421+
ERROR("param parsing error: expected number, got \"%s\"\n", value);
422+
exit(1);
423+
}
424+
while (*end == ' ') end++;
425+
if (*end == '\0') return n;
426+
427+
int64_t mult = 1;
428+
if (strcasecmp(end, "B") == 0) {
429+
mult = 1;
430+
} else if (strcasecmp(end, "K") == 0 || strcasecmp(end, "KB") == 0) {
431+
mult = 1024LL;
432+
} else if (strcasecmp(end, "M") == 0 || strcasecmp(end, "MB") == 0) {
433+
mult = 1024LL * 1024;
434+
} else if (strcasecmp(end, "G") == 0 || strcasecmp(end, "GB") == 0) {
435+
mult = 1024LL * 1024 * 1024;
436+
} else if (strcasecmp(end, "T") == 0 || strcasecmp(end, "TB") == 0) {
437+
mult = 1024LL * 1024 * 1024 * 1024;
438+
} else {
439+
ERROR("param parsing error: unknown size suffix \"%s\"\n", end);
440+
exit(1);
441+
}
442+
return n * mult;
443+
}
444+
365445
static void FIFO_Reinsertion_parse_params(cache_t *cache,
366446
const char *cache_specific_params) {
367447
FIFO_Reinsertion_params_t *params =
368448
(FIFO_Reinsertion_params_t *)cache->eviction_params;
369449

370450
char *params_str = strdup(cache_specific_params);
371451
char *old_params_str = params_str;
372-
char *end;
373452

374453
while (params_str != NULL && params_str[0] != '\0') {
375454
/* different parameters are separated by comma,
@@ -391,21 +470,15 @@ static void FIFO_Reinsertion_parse_params(cache_t *cache,
391470
params->retain_policy = RETAIN_POLICY_BELADY;
392471
else if (strcasecmp(value, "none") == 0) {
393472
params->retain_policy = RETAIN_NONE;
394-
params->n_keep_obj = 0;
473+
params->n_keep_byte = 0;
395474
} else {
396475
ERROR("unknown retain-policy %s\n", value);
397476
exit(1);
398477
}
399-
} else if (strcasecmp(key, "n-exam") == 0) {
400-
params->n_exam_obj = (int)strtol(value, &end, 0);
401-
if (strlen(end) > 2) {
402-
ERROR("param parsing error, find string \"%s\" after number\n", end);
403-
}
404-
} else if (strcasecmp(key, "n-keep") == 0) {
405-
params->n_keep_obj = (int)strtol(value, &end, 0);
406-
if (strlen(end) > 2) {
407-
ERROR("param parsing error, find string \"%s\" after number\n", end);
408-
}
478+
} else if (strcasecmp(key, "n-exam-byte") == 0) {
479+
params->n_exam_byte = FIFO_Reinsertion_parse_byte_size(value);
480+
} else if (strcasecmp(key, "n-keep-byte") == 0) {
481+
params->n_keep_byte = FIFO_Reinsertion_parse_byte_size(value);
409482
} else if (strcasecmp(key, "print") == 0) {
410483
printf("%s parameters: %s\n", cache->cache_name,
411484
FIFO_Reinsertion_current_params(params));

0 commit comments

Comments
 (0)