Skip to content

Commit 9451cb8

Browse files
committed
Add retain ratio parameter to FIFO eviction algorithms for improved object retention control
1 parent ebf086e commit 9451cb8

2 files changed

Lines changed: 104 additions & 144 deletions

File tree

libCacheSim/cache/eviction/FIFO_Merge.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ typedef struct FIFO_Merge_params {
4343
int n_exam_obj;
4444
// of the n_exam_obj, we keep n_keep_obj and evict the rest
4545
int n_keep_obj;
46+
47+
double retain_ratio;
4648
// used to sort the n_exam_obj objects
4749
struct sort_list_node *metric_list;
4850
// the policy to determine the n_keep_obj objects
@@ -113,8 +115,9 @@ cache_t *FIFO_Merge_init(const common_cache_params_t ccache_params,
113115

114116
/* TODO: can we make this parameter adaptive to trace? */
115117
params->n_exam_obj = 100;
116-
params->n_keep_obj = params->n_exam_obj / 2;
117-
params->retain_policy = RETAIN_POLICY_FREQUENCY;
118+
params->retain_ratio = 0.25;
119+
params->n_keep_obj = (int)(params->n_exam_obj * params->retain_ratio);
120+
params->retain_policy = RETAIN_POLICY_RECENCY;
118121
params->next_to_exam = NULL;
119122
params->pos_in_metric_list = INT32_MAX;
120123

@@ -125,8 +128,8 @@ cache_t *FIFO_Merge_init(const common_cache_params_t ccache_params,
125128
assert(params->n_exam_obj > 0 && params->n_keep_obj >= 0);
126129
assert(params->n_keep_obj <= params->n_exam_obj);
127130

128-
snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "FIFO_Merge_%s",
129-
retain_policy_names[params->retain_policy]);
131+
snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "FIFO_Merge_%s-%.4lf",
132+
retain_policy_names[params->retain_policy], params->retain_ratio);
130133
params->metric_list = my_malloc_n(struct sort_list_node, params->n_exam_obj);
131134

132135
return cache;
@@ -378,8 +381,8 @@ static void FIFO_Merge_parse_params(cache_t *cache,
378381
if (strlen(end) > 2) {
379382
ERROR("param parsing error, find string \"%s\" after number\n", end);
380383
}
381-
} else if (strcasecmp(key, "n-keep") == 0) {
382-
params->n_keep_obj = (int)strtol(value, &end, 0);
384+
} else if (strcasecmp(key, "retain-ratio") == 0) {
385+
params->retain_ratio = strtod(value, &end);
383386
if (strlen(end) > 2) {
384387
ERROR("param parsing error, find string \"%s\" after number\n", end);
385388
}

libCacheSim/cache/eviction/FIFO_Reinsertion.c

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

4242
// points to the eviction position
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
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+
49+
double retain_ratio;
50+
// used to sort the n_exam_obj objects
4951
struct sort_list_node *metric_list;
50-
// current allocated capacity (in entries) of metric_list
51-
int metric_list_capacity;
52-
// the policy to determine which objects to keep
52+
// the policy to determine the n_keep_obj objects
5353
retain_policy_t retain_policy;
54+
5455
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;
5956

6057
int64_t n_obj_rewritten;
6158
int64_t n_byte_rewritten;
@@ -125,30 +122,25 @@ cache_t *FIFO_Reinsertion_init(const common_cache_params_t ccache_params,
125122
memset(params, 0, sizeof(FIFO_Reinsertion_params_t));
126123
cache->eviction_params = params;
127124

128-
params->n_exam_byte = 100LL * 1024 * 1024; // 100 MB
129-
params->n_keep_byte = 25LL * 1024 * 1024; // 25 MB
125+
params->n_exam_obj = 100;
126+
params->retain_ratio = 0.25;
127+
params->n_keep_obj = (int)(params->n_exam_obj * params->retain_ratio);
130128
params->retain_policy = RETAIN_POLICY_RECENCY;
131-
params->next_to_exam = NULL;
129+
params->next_to_merge = NULL;
130+
params->pos_in_metric_list = INT32_MAX;
132131
params->q_head = NULL;
133132
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;
137133

138134
if (cache_specific_params != NULL) {
139135
FIFO_Reinsertion_parse_params(cache, cache_specific_params);
140136
}
141137

142-
assert(params->n_exam_byte > 0 && params->n_keep_byte >= 0);
143-
assert(params->n_keep_byte <= params->n_exam_byte);
138+
assert(params->n_exam_obj > 0 && params->n_keep_obj >= 0);
139+
assert(params->n_keep_obj <= params->n_exam_obj);
144140

145141
snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "FIFO_Reinsertion_%s-%.4lf",
146-
retain_policy_names[params->retain_policy],
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);
142+
retain_policy_names[params->retain_policy], params->retain_ratio);
143+
params->metric_list = my_malloc_n(struct sort_list_node, params->n_exam_obj);
152144

153145
return cache;
154146
}
@@ -161,7 +153,7 @@ cache_t *FIFO_Reinsertion_init(const common_cache_params_t ccache_params,
161153
static void FIFO_Reinsertion_free(cache_t *cache) {
162154
FIFO_Reinsertion_params_t *params =
163155
(FIFO_Reinsertion_params_t *)cache->eviction_params;
164-
my_free(sizeof(struct sort_list_node) * params->metric_list_capacity,
156+
my_free(sizeof(struct sort_list_node) * params->n_exam_obj,
165157
params->metric_list);
166158
my_free(sizeof(FIFO_Reinsertion_params_t), params);
167159
cache_struct_free(cache);
@@ -273,94 +265,84 @@ static void FIFO_Reinsertion_evict(cache_t *cache, const request_t *req) {
273265
FIFO_Reinsertion_params_t *params =
274266
(FIFO_Reinsertion_params_t *)cache->eviction_params;
275267
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) {
268+
int n_evict = params->n_exam_obj - params->n_keep_obj;
269+
270+
// check if we have objects identified to evict from a previous scan
271+
if (params->pos_in_metric_list < n_evict) {
284272
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);
273+
FIFO_Reinsertion_remove_obj(cache, cache_obj);
274+
275+
// if this was the last eviction in the batch, reinsert the kept objects
276+
if (params->pos_in_metric_list >= n_evict) {
277+
for (int i = n_evict; i < params->n_exam_obj; i++) {
278+
cache_obj = params->metric_list[i].cache_obj;
279+
move_obj_to_head(&params->q_head, &params->q_tail, cache_obj);
280+
cache_obj->FIFO_Reinsertion.freq =
281+
(cache_obj->FIFO_Reinsertion.freq + 1) / 2;
282+
283+
params->n_obj_rewritten += 1;
284+
params->n_byte_rewritten += cache_obj->obj_size;
285+
}
286+
params->pos_in_metric_list = INT32_MAX;
287+
}
288288
return;
289289
}
290290

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);
291+
if (cache->n_obj <= params->n_exam_obj) {
292+
// just evict one object - this is fifo
293+
cache_obj = params->next_to_merge;
294+
if (cache_obj == NULL) {
295+
cache_obj = params->q_tail;
296+
}
297+
params->next_to_merge = cache_obj->queue.prev;
298+
FIFO_Reinsertion_remove_obj(cache, cache_obj);
297299
return;
298300
}
299301

300-
// collect ~n_exam_byte worth of objects from the merge cursor
302+
// collect metric for n_exam obj, we will keep objects with larger metric
301303
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) {
304+
cache_obj = params->next_to_merge;
305+
if (cache_obj == NULL) {
306+
params->next_to_merge = params->q_tail;
307+
cache_obj = params->q_tail;
308+
n_loop = 1;
309+
}
310+
311+
for (int i = 0; i < params->n_exam_obj; i++) {
312+
assert(cache_obj != NULL);
313+
params->metric_list[i].metric = retain_metric(cache, cache_obj);
314+
params->metric_list[i].cache_obj = cache_obj;
315+
cache_obj = cache_obj->queue.prev;
316+
306317
if (cache_obj == NULL) {
307318
cache_obj = params->q_tail;
308-
n_loop += 1;
309-
DEBUG_ASSERT(n_loop <= 2);
310-
}
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;
319+
DEBUG_ASSERT(n_loop++ <= 2);
323320
}
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;
330321
}
331-
params->next_to_exam = cache_obj;
322+
params->next_to_merge = cache_obj;
332323

333-
// sort by metric ascending - low metric (least worth keeping) first
334-
qsort(params->metric_list, n_collected, sizeof(struct sort_list_node),
324+
// sort metrics
325+
qsort(params->metric_list, params->n_exam_obj, sizeof(struct sort_list_node),
335326
cmp_list_node);
336327

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++;
343-
}
344-
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++) {
347-
cache_obj = params->metric_list[i].cache_obj;
348-
move_obj_to_head(&params->q_head, &params->q_tail, cache_obj);
349-
cache_obj->FIFO_Reinsertion.freq =
350-
(cache_obj->FIFO_Reinsertion.freq + 1) / 2;
351-
params->n_obj_rewritten += 1;
352-
params->n_byte_rewritten += cache_obj->obj_size;
353-
}
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;
328+
// evict the first object, save state for subsequent calls
358329
params->pos_in_metric_list = 1;
359-
360330
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);
331+
FIFO_Reinsertion_remove_obj(cache, cache_obj);
332+
333+
// if only one object to evict in the batch, reinsert the kept objects now
334+
if (params->pos_in_metric_list >= n_evict) {
335+
for (int i = n_evict; i < params->n_exam_obj; i++) {
336+
cache_obj = params->metric_list[i].cache_obj;
337+
move_obj_to_head(&params->q_head, &params->q_tail, cache_obj);
338+
cache_obj->FIFO_Reinsertion.freq =
339+
(cache_obj->FIFO_Reinsertion.freq + 1) / 2;
340+
341+
params->n_obj_rewritten += 1;
342+
params->n_byte_rewritten += cache_obj->obj_size;
343+
}
344+
params->pos_in_metric_list = INT32_MAX;
345+
}
364346
}
365347

366348
static void FIFO_Reinsertion_remove_obj(cache_t *cache, cache_obj_t *obj) {
@@ -404,51 +386,20 @@ static bool FIFO_Reinsertion_remove(cache_t *cache, obj_id_t obj_id) {
404386
static const char *FIFO_Reinsertion_current_params(
405387
FIFO_Reinsertion_params_t *params) {
406388
static __thread char params_str[128];
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,
389+
snprintf(params_str, 128, "n-exam=%d, n-keep=%d, retain-policy=%s",
390+
params->n_exam_obj, params->n_keep_obj,
410391
retain_policy_names[params->retain_policy]);
411392
return params_str;
412393
}
413394

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-
445395
static void FIFO_Reinsertion_parse_params(cache_t *cache,
446396
const char *cache_specific_params) {
447397
FIFO_Reinsertion_params_t *params =
448398
(FIFO_Reinsertion_params_t *)cache->eviction_params;
449399

450400
char *params_str = strdup(cache_specific_params);
451401
char *old_params_str = params_str;
402+
char *end;
452403

453404
while (params_str != NULL && params_str[0] != '\0') {
454405
/* different parameters are separated by comma,
@@ -470,15 +421,21 @@ static void FIFO_Reinsertion_parse_params(cache_t *cache,
470421
params->retain_policy = RETAIN_POLICY_BELADY;
471422
else if (strcasecmp(value, "none") == 0) {
472423
params->retain_policy = RETAIN_NONE;
473-
params->n_keep_byte = 0;
424+
params->n_keep_obj = 0;
474425
} else {
475426
ERROR("unknown retain-policy %s\n", value);
476427
exit(1);
477428
}
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);
429+
} else if (strcasecmp(key, "n-exam") == 0) {
430+
params->n_exam_obj = (int)strtol(value, &end, 0);
431+
if (strlen(end) > 2) {
432+
ERROR("param parsing error, find string \"%s\" after number\n", end);
433+
}
434+
} else if (strcasecmp(key, "retain-ratio") == 0) {
435+
params->retain_ratio = strtod(value, &end);
436+
if (strlen(end) > 2) {
437+
ERROR("param parsing error, find string \"%s\" after number\n", end);
438+
}
482439
} else if (strcasecmp(key, "print") == 0) {
483440
printf("%s parameters: %s\n", cache->cache_name,
484441
FIFO_Reinsertion_current_params(params));

0 commit comments

Comments
 (0)