|
| 1 | +// |
| 2 | +// Multi-Queue (MQ) cache eviction policy. |
| 3 | +// Objects are tracked in multiple LRU queues based on logarithmic frequency. |
| 4 | +// |
| 5 | + |
| 6 | +#include "dataStructure/hashtable/hashtable.h" |
| 7 | +#include "libCacheSim/evictionAlgo.h" |
| 8 | + |
| 9 | +#ifdef __cplusplus |
| 10 | +extern "C" { |
| 11 | +#endif |
| 12 | + |
| 13 | +#define MQ_MAX_N_QUEUE 32 |
| 14 | + |
| 15 | +typedef struct { |
| 16 | + cache_obj_t **q_heads; |
| 17 | + cache_obj_t **q_tails; |
| 18 | + int64_t *q_n_bytes; |
| 19 | + int64_t *q_n_objs; |
| 20 | + int n_queue; |
| 21 | +} MQ_params_t; |
| 22 | + |
| 23 | +static const char *DEFAULT_CACHE_PARAMS = "n-queue=8"; |
| 24 | + |
| 25 | +static void MQ_free(cache_t *cache); |
| 26 | +static bool MQ_get(cache_t *cache, const request_t *req); |
| 27 | +static cache_obj_t *MQ_find(cache_t *cache, const request_t *req, |
| 28 | + bool update_cache); |
| 29 | +static cache_obj_t *MQ_insert(cache_t *cache, const request_t *req); |
| 30 | +static cache_obj_t *MQ_to_evict(cache_t *cache, const request_t *req); |
| 31 | +static void MQ_evict(cache_t *cache, const request_t *req); |
| 32 | +static bool MQ_remove(cache_t *cache, obj_id_t obj_id); |
| 33 | + |
| 34 | +static void MQ_remove_obj(cache_t *cache, cache_obj_t *obj); |
| 35 | +static void MQ_parse_params(cache_t *cache, const char *cache_specific_params); |
| 36 | + |
| 37 | +static inline int MQ_level(int64_t freq, int n_queue) { |
| 38 | + int level = 0; |
| 39 | + while (freq > 1 && level < n_queue - 1) { |
| 40 | + freq >>= 1; |
| 41 | + level++; |
| 42 | + } |
| 43 | + return level; |
| 44 | +} |
| 45 | + |
| 46 | +cache_t *MultiQueue_init(const common_cache_params_t ccache_params, |
| 47 | + const char *cache_specific_params) { |
| 48 | + cache_t *cache = |
| 49 | + cache_struct_init("MultiQueue", ccache_params, cache_specific_params); |
| 50 | + cache->cache_init = MultiQueue_init; |
| 51 | + cache->cache_free = MQ_free; |
| 52 | + cache->get = MQ_get; |
| 53 | + cache->find = MQ_find; |
| 54 | + cache->insert = MQ_insert; |
| 55 | + cache->evict = MQ_evict; |
| 56 | + cache->remove = MQ_remove; |
| 57 | + cache->to_evict = MQ_to_evict; |
| 58 | + cache->get_occupied_byte = cache_get_occupied_byte_default; |
| 59 | + cache->can_insert = cache_can_insert_default; |
| 60 | + cache->get_n_obj = cache_get_n_obj_default; |
| 61 | + |
| 62 | + if (ccache_params.consider_obj_metadata) { |
| 63 | + cache->obj_md_size = 8 * 2; |
| 64 | + } else { |
| 65 | + cache->obj_md_size = 0; |
| 66 | + } |
| 67 | + |
| 68 | + cache->eviction_params = malloc(sizeof(MQ_params_t)); |
| 69 | + memset(cache->eviction_params, 0, sizeof(MQ_params_t)); |
| 70 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 71 | + |
| 72 | + params->n_queue = 8; |
| 73 | + MQ_parse_params(cache, DEFAULT_CACHE_PARAMS); |
| 74 | + if (cache_specific_params != NULL) { |
| 75 | + MQ_parse_params(cache, cache_specific_params); |
| 76 | + } |
| 77 | + |
| 78 | + params->q_heads = calloc(params->n_queue, sizeof(cache_obj_t *)); |
| 79 | + params->q_tails = calloc(params->n_queue, sizeof(cache_obj_t *)); |
| 80 | + params->q_n_bytes = calloc(params->n_queue, sizeof(int64_t)); |
| 81 | + params->q_n_objs = calloc(params->n_queue, sizeof(int64_t)); |
| 82 | + |
| 83 | + snprintf(cache->cache_name, CACHE_NAME_ARRAY_LEN, "MQ(%d)", params->n_queue); |
| 84 | + |
| 85 | + return cache; |
| 86 | +} |
| 87 | + |
| 88 | +static void MQ_free(cache_t *cache) { |
| 89 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 90 | + free(params->q_heads); |
| 91 | + free(params->q_tails); |
| 92 | + free(params->q_n_bytes); |
| 93 | + free(params->q_n_objs); |
| 94 | + free(params); |
| 95 | + cache_struct_free(cache); |
| 96 | +} |
| 97 | + |
| 98 | +static bool MQ_get(cache_t *cache, const request_t *req) { |
| 99 | + return cache_get_base(cache, req); |
| 100 | +} |
| 101 | + |
| 102 | +static cache_obj_t *MQ_find(cache_t *cache, const request_t *req, |
| 103 | + bool update_cache) { |
| 104 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 105 | + cache_obj_t *obj = cache_find_base(cache, req, update_cache); |
| 106 | + if (!obj || !update_cache) { |
| 107 | + return obj; |
| 108 | + } |
| 109 | + |
| 110 | + obj->misc.freq += 1; |
| 111 | + int curr_level = obj->SLRU.lru_id; |
| 112 | + int next_level = MQ_level(obj->misc.freq, params->n_queue); |
| 113 | + |
| 114 | + if (next_level != curr_level) { |
| 115 | + remove_obj_from_list(¶ms->q_heads[curr_level], ¶ms->q_tails[curr_level], |
| 116 | + obj); |
| 117 | + params->q_n_objs[curr_level] -= 1; |
| 118 | + params->q_n_bytes[curr_level] -= obj->obj_size; |
| 119 | + |
| 120 | + prepend_obj_to_head(¶ms->q_heads[next_level], ¶ms->q_tails[next_level], |
| 121 | + obj); |
| 122 | + params->q_n_objs[next_level] += 1; |
| 123 | + params->q_n_bytes[next_level] += obj->obj_size; |
| 124 | + obj->SLRU.lru_id = next_level; |
| 125 | + } else { |
| 126 | + move_obj_to_head(¶ms->q_heads[curr_level], ¶ms->q_tails[curr_level], |
| 127 | + obj); |
| 128 | + } |
| 129 | + |
| 130 | + return obj; |
| 131 | +} |
| 132 | + |
| 133 | +static cache_obj_t *MQ_insert(cache_t *cache, const request_t *req) { |
| 134 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 135 | + cache_obj_t *obj = cache_insert_base(cache, req); |
| 136 | + |
| 137 | + obj->misc.freq = 1; |
| 138 | + obj->SLRU.lru_id = 0; |
| 139 | + prepend_obj_to_head(¶ms->q_heads[0], ¶ms->q_tails[0], obj); |
| 140 | + params->q_n_objs[0] += 1; |
| 141 | + params->q_n_bytes[0] += obj->obj_size; |
| 142 | + |
| 143 | + return obj; |
| 144 | +} |
| 145 | + |
| 146 | +static cache_obj_t *MQ_to_evict(cache_t *cache, const request_t *req) { |
| 147 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 148 | + (void)req; |
| 149 | + |
| 150 | + for (int i = 0; i < params->n_queue; i++) { |
| 151 | + if (params->q_tails[i] != NULL) { |
| 152 | + cache->to_evict_candidate_gen_vtime = cache->n_req; |
| 153 | + return params->q_tails[i]; |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + DEBUG_ASSERT(cache->occupied_byte == 0); |
| 158 | + return NULL; |
| 159 | +} |
| 160 | + |
| 161 | +static void MQ_evict(cache_t *cache, const request_t *req) { |
| 162 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 163 | + cache_obj_t *obj_to_evict = MQ_to_evict(cache, req); |
| 164 | + DEBUG_ASSERT(obj_to_evict != NULL); |
| 165 | + |
| 166 | + int level = obj_to_evict->SLRU.lru_id; |
| 167 | + remove_obj_from_list(¶ms->q_heads[level], ¶ms->q_tails[level], |
| 168 | + obj_to_evict); |
| 169 | + params->q_n_objs[level] -= 1; |
| 170 | + params->q_n_bytes[level] -= obj_to_evict->obj_size; |
| 171 | + |
| 172 | + cache_evict_base(cache, obj_to_evict, true); |
| 173 | +} |
| 174 | + |
| 175 | +static void MQ_remove_obj(cache_t *cache, cache_obj_t *obj) { |
| 176 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 177 | + int level = obj->SLRU.lru_id; |
| 178 | + |
| 179 | + remove_obj_from_list(¶ms->q_heads[level], ¶ms->q_tails[level], obj); |
| 180 | + params->q_n_objs[level] -= 1; |
| 181 | + params->q_n_bytes[level] -= obj->obj_size; |
| 182 | + |
| 183 | + cache_remove_obj_base(cache, obj, true); |
| 184 | +} |
| 185 | + |
| 186 | +static bool MQ_remove(cache_t *cache, obj_id_t obj_id) { |
| 187 | + cache_obj_t *obj = hashtable_find_obj_id(cache->hashtable, obj_id); |
| 188 | + if (obj == NULL) { |
| 189 | + return false; |
| 190 | + } |
| 191 | + |
| 192 | + MQ_remove_obj(cache, obj); |
| 193 | + return true; |
| 194 | +} |
| 195 | + |
| 196 | +static void MQ_parse_params(cache_t *cache, const char *cache_specific_params) { |
| 197 | + MQ_params_t *params = (MQ_params_t *)cache->eviction_params; |
| 198 | + |
| 199 | + char *params_str = strdup(cache_specific_params); |
| 200 | + char *params_str_to_free = params_str; |
| 201 | + |
| 202 | + while (params_str != NULL && params_str[0] != '\0') { |
| 203 | + char *key = strsep((char **)¶ms_str, "="); |
| 204 | + char *value = strsep((char **)¶ms_str, ","); |
| 205 | + |
| 206 | + if (strcasecmp(key, "n-queue") == 0 || strcasecmp(key, "n-queues") == 0 || |
| 207 | + strcasecmp(key, "nq") == 0) { |
| 208 | + params->n_queue = (int)strtol(value, NULL, 0); |
| 209 | + if (params->n_queue <= 0 || params->n_queue > MQ_MAX_N_QUEUE) { |
| 210 | + ERROR("MultiQueue n-queue should be in [1, %d], given %d\n", |
| 211 | + MQ_MAX_N_QUEUE, params->n_queue); |
| 212 | + abort(); |
| 213 | + } |
| 214 | + } else { |
| 215 | + WARN("MQ does not support parameter %s\n", key); |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + free(params_str_to_free); |
| 220 | +} |
| 221 | + |
| 222 | +#ifdef __cplusplus |
| 223 | +} |
| 224 | +#endif |
0 commit comments