Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ cachesim supports the following algorithms:
### Eviction algorithms
* [FIFO](/libCacheSim/cache/eviction/FIFO.c), [LRU](/libCacheSim/cache/eviction/LRU.c), [Clock](/libCacheSim/cache/eviction/Clock.c), [SLRU](/libCacheSim/cache/eviction/SLRU.c)
* [LFU](/libCacheSim/cache/eviction/LFU.c), [LFU with dynamic aging](/libCacheSim/cache/eviction/LFUDA.c)
* [ARC](/libCacheSim/cache/eviction/ARC.c), [TwoQ](/libCacheSim/cache/eviction/TwoQ.c), [CLOCK-PRO](/libCacheSim/cache/eviction/ClockPro.c)
* [ARC](/libCacheSim/cache/eviction/ARC.c), [TwoQ](/libCacheSim/cache/eviction/TwoQ.c), [MultiQueue](/libCacheSim/cache/eviction/MultiQueue.c), [CLOCK-PRO](/libCacheSim/cache/eviction/ClockPro.c)
* [Belady](/libCacheSim/cache/eviction/Belady.c), [BeladySize](/libCacheSim/cache/eviction/BeladySize.c)
* [GDSF](/libCacheSim/cache/eviction/cpp/GDSF.cpp)
* [Hyperbolic](/libCacheSim/cache/eviction/Hyperbolic.c)
Expand Down
3 changes: 3 additions & 0 deletions libCacheSim/bin/cachesim/cache_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ static inline cache_t *create_cache(const char *trace_path,
{"lru", LRU_init},
{"lru-prob", LRU_Prob_init},
{"nop", nop_init},
{"mq", MultiQueue_init},
{"multi-queue", MultiQueue_init},
{"multiqueue", MultiQueue_init},
// plugin cache that allows user to implement custom cache
{"pluginCache", pluginCache_init},
{"qdlp", QDLP_init},
Expand Down
1 change: 1 addition & 0 deletions libCacheSim/cache/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ set(eviction_sources_c
eviction/LRUProb.c # a probabilistic version of LRU
eviction/LRUv0.c # an inefficient version but easier to understand
eviction/MRU.c
eviction/MultiQueue.c
eviction/nop.c
eviction/plugin_cache.c # plugin cache that allows user to implement custom cache
eviction/QDLP.c
Expand Down
224 changes: 224 additions & 0 deletions libCacheSim/cache/eviction/MultiQueue.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//
// Multi-Queue (MQ) cache eviction policy.
// Objects are tracked in multiple LRU queues based on logarithmic frequency.
//

#include "dataStructure/hashtable/hashtable.h"
#include "libCacheSim/evictionAlgo.h"

#ifdef __cplusplus
extern "C" {
#endif

#define MQ_MAX_N_QUEUE 32

typedef struct {
cache_obj_t **q_heads;
cache_obj_t **q_tails;
int64_t *q_n_bytes;
int64_t *q_n_objs;
int n_queue;
} MQ_params_t;

static const char *DEFAULT_CACHE_PARAMS = "n-queue=8";

static void MQ_free(cache_t *cache);
static bool MQ_get(cache_t *cache, const request_t *req);
static cache_obj_t *MQ_find(cache_t *cache, const request_t *req,
bool update_cache);
static cache_obj_t *MQ_insert(cache_t *cache, const request_t *req);
static cache_obj_t *MQ_to_evict(cache_t *cache, const request_t *req);
static void MQ_evict(cache_t *cache, const request_t *req);
static bool MQ_remove(cache_t *cache, obj_id_t obj_id);

static void MQ_remove_obj(cache_t *cache, cache_obj_t *obj);
static void MQ_parse_params(cache_t *cache, const char *cache_specific_params);

static inline int MQ_level(int64_t freq, int n_queue) {
int level = 0;
while (freq > 1 && level < n_queue - 1) {
freq >>= 1;
level++;
}
return level;
}

cache_t *MultiQueue_init(const common_cache_params_t ccache_params,
const char *cache_specific_params) {
cache_t *cache =
cache_struct_init("MultiQueue", ccache_params, cache_specific_params);
cache->cache_init = MultiQueue_init;
cache->cache_free = MQ_free;
cache->get = MQ_get;
cache->find = MQ_find;
cache->insert = MQ_insert;
cache->evict = MQ_evict;
cache->remove = MQ_remove;
cache->to_evict = MQ_to_evict;
cache->get_occupied_byte = cache_get_occupied_byte_default;
cache->can_insert = cache_can_insert_default;
cache->get_n_obj = cache_get_n_obj_default;

if (ccache_params.consider_obj_metadata) {
cache->obj_md_size = 8 * 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The metadata size is hardcoded as 8 * 2. This assumes a pointer size of 8 bytes, which is not portable across different architectures (e.g., 32-bit systems). It's better to use sizeof(cache_obj_t *) * 2 to calculate the size of the prev and next pointers dynamically, ensuring portability.

Suggested change
cache->obj_md_size = 8 * 2;
cache->obj_md_size = sizeof(cache_obj_t *) * 2;

} else {
cache->obj_md_size = 0;
}

cache->eviction_params = malloc(sizeof(MQ_params_t));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The memory allocated by malloc is not checked for NULL. If malloc fails, this will result in a NULL pointer dereference later, causing a crash. Please add a check for the return value and handle the error, for example by calling abort() as is done elsewhere in the project.

memset(cache->eviction_params, 0, sizeof(MQ_params_t));
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;

params->n_queue = 8;
MQ_parse_params(cache, DEFAULT_CACHE_PARAMS);
if (cache_specific_params != NULL) {
MQ_parse_params(cache, cache_specific_params);
}

params->q_heads = calloc(params->n_queue, sizeof(cache_obj_t *));
params->q_tails = calloc(params->n_queue, sizeof(cache_obj_t *));
params->q_n_bytes = calloc(params->n_queue, sizeof(int64_t));
params->q_n_objs = calloc(params->n_queue, sizeof(int64_t));
Comment on lines +78 to +81
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The memory allocated by these calloc calls is not checked for NULL. An allocation failure would lead to a crash when the returned pointers are dereferenced. Please add NULL checks for each allocation and handle potential failures gracefully.


snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "MQ(%d)", params->n_queue);

return cache;
}
Comment on lines +46 to +86

static void MQ_free(cache_t *cache) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;
free(params->q_heads);
free(params->q_tails);
free(params->q_n_bytes);
free(params->q_n_objs);
free(params);
cache_struct_free(cache);
}

static bool MQ_get(cache_t *cache, const request_t *req) {
return cache_get_base(cache, req);
}

static cache_obj_t *MQ_find(cache_t *cache, const request_t *req,
bool update_cache) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;
cache_obj_t *obj = cache_find_base(cache, req, update_cache);
if (!obj || !update_cache) {
return obj;
}

obj->misc.freq += 1;
int curr_level = obj->SLRU.lru_id;
int next_level = MQ_level(obj->misc.freq, params->n_queue);

if (next_level != curr_level) {
remove_obj_from_list(&params->q_heads[curr_level], &params->q_tails[curr_level],
obj);
params->q_n_objs[curr_level] -= 1;
params->q_n_bytes[curr_level] -= obj->obj_size;

prepend_obj_to_head(&params->q_heads[next_level], &params->q_tails[next_level],
obj);
params->q_n_objs[next_level] += 1;
params->q_n_bytes[next_level] += obj->obj_size;
obj->SLRU.lru_id = next_level;
} else {
move_obj_to_head(&params->q_heads[curr_level], &params->q_tails[curr_level],
obj);
}

return obj;
}

static cache_obj_t *MQ_insert(cache_t *cache, const request_t *req) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;
cache_obj_t *obj = cache_insert_base(cache, req);

obj->misc.freq = 1;
obj->SLRU.lru_id = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The SLRU.lru_id field is being reused to store the queue level for an object. While this appears to be a common pattern in this codebase to avoid modifying cache_obj_t, it's not immediately obvious. Adding a comment here would improve code clarity and maintainability for future developers.

Suggested change
obj->SLRU.lru_id = 0;
obj->SLRU.lru_id = 0; // Use SLRU field to store MQ level

prepend_obj_to_head(&params->q_heads[0], &params->q_tails[0], obj);
params->q_n_objs[0] += 1;
params->q_n_bytes[0] += obj->obj_size;

return obj;
}

static cache_obj_t *MQ_to_evict(cache_t *cache, const request_t *req) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;
(void)req;

for (int i = 0; i < params->n_queue; i++) {
if (params->q_tails[i] != NULL) {
cache->to_evict_candidate_gen_vtime = cache->n_req;
return params->q_tails[i];
}
}

DEBUG_ASSERT(cache->occupied_byte == 0);
return NULL;
}

static void MQ_evict(cache_t *cache, const request_t *req) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;
cache_obj_t *obj_to_evict = MQ_to_evict(cache, req);
DEBUG_ASSERT(obj_to_evict != NULL);

int level = obj_to_evict->SLRU.lru_id;
remove_obj_from_list(&params->q_heads[level], &params->q_tails[level],
obj_to_evict);
params->q_n_objs[level] -= 1;
params->q_n_bytes[level] -= obj_to_evict->obj_size;

cache_evict_base(cache, obj_to_evict, true);
}

static void MQ_remove_obj(cache_t *cache, cache_obj_t *obj) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;
int level = obj->SLRU.lru_id;

remove_obj_from_list(&params->q_heads[level], &params->q_tails[level], obj);
params->q_n_objs[level] -= 1;
params->q_n_bytes[level] -= obj->obj_size;

cache_remove_obj_base(cache, obj, true);
}

static bool MQ_remove(cache_t *cache, obj_id_t obj_id) {
cache_obj_t *obj = hashtable_find_obj_id(cache->hashtable, obj_id);
if (obj == NULL) {
return false;
}

MQ_remove_obj(cache, obj);
return true;
}

static void MQ_parse_params(cache_t *cache, const char *cache_specific_params) {
MQ_params_t *params = (MQ_params_t *)cache->eviction_params;

char *params_str = strdup(cache_specific_params);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

strdup can return NULL if memory allocation fails. This would cause a crash in the while loop condition or when strsep is called. Please add a NULL check for params_str after this line.

char *params_str_to_free = params_str;

while (params_str != NULL && params_str[0] != '\0') {
char *key = strsep((char **)&params_str, "=");
char *value = strsep((char **)&params_str, ",");

if (strcasecmp(key, "n-queue") == 0 || strcasecmp(key, "n-queues") == 0 ||
strcasecmp(key, "nq") == 0) {
params->n_queue = (int)strtol(value, NULL, 0);
Comment on lines +202 to +208
if (params->n_queue <= 0 || params->n_queue > MQ_MAX_N_QUEUE) {
ERROR("MultiQueue n-queue should be in [1, %d], given %d\n",
MQ_MAX_N_QUEUE, params->n_queue);
abort();
}
} else {
WARN("MQ does not support parameter %s\n", key);
}
}

free(params_str_to_free);
}

#ifdef __cplusplus
}
#endif
3 changes: 3 additions & 0 deletions libCacheSim/include/libCacheSim/evictionAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ cache_t *LRUv0_init(const common_cache_params_t ccache_params,
cache_t *MRU_init(const common_cache_params_t ccache_params,
const char *cache_specific_params);

cache_t *MultiQueue_init(const common_cache_params_t ccache_params,
const char *cache_specific_params);

cache_t *nop_init(const common_cache_params_t ccache_params,
const char *cache_specific_params);

Expand Down
Loading